File evsieve-1.4.0~0.obscpio of Package evsieve

07070100000000000081A4000000000000000000000001647DB23000000010000000000000000000000000000000000000001B00000000evsieve-1.4.0~0/.gitignore/target
/local

07070100000001000081A4000000000000000000000001647DB23000000B96000000000000000000000000000000000000001800000000evsieve-1.4.0~0/COPYINGEvsieve, Copyright © 2021, Kars Mulder

Unless mentioned otherwise, all parts of this program are available under
the GNU General Public License version 2.0, or at your option, any later
version, provided that your choosing to follow a later version shall not
impose any additional obligations on any author or copyright holder of
this program.

A copy of the GNU General Public License version 2.0 can be found in the
"LICENSE" file. This program is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

This program may contain parts derived from third-party programs or
libraries. The General Public License does not necessarily apply to any
third party content in this program or on this repository. All third-party
components are licensed under their original licenses.


The source code file "src/bindings/libevdev.rs" was programmatically
derived from header files belonging to the "libevdev" library. The license
applying to those files can be found in the file "licenses/thirdparty/
libevdev/header.txt". Those libevdev headers on their turn include some
content from Linux kernel header files, such as input event codes and some
data structures. The Linux kernel is available under the GNU General Public
License version 2.0 with the Linux syscall note. A notice from the kernel
header file containing input event codes can be found in "licenses/thirdparty/
linux/linux_kernel_input_codes_notice.txt". A copy of the Linux syscall
note can be found in "licenses/thirdparty/linux/linux_syscall_note.txt".

Once compiled, this program will link against the libevdev library. The
license of the libevdev library can be found in "licenses/thirdparty/
libevdev/license.txt".

Once compiled, this program may link against the libsystemd library. The
libsystemd library is licensed under the GNU Lesser General Public License
version 2.1 or later. A copy of this licence can be found in "licenses/
thirdparty/libsystemd/lgpl.txt".

Once compiled, this program will statically link against the Rust standard
library. The Rust standard library is generally dual-licensed under the
MIT license and the Apache 2.0 license. These licenses can be found in
"licenses/thirdparty/rust". The Rust project may contain parts under
different licenses. See "licenses/thirdparty/rust/COPYRIGHT" for a file
taken from the Rust project describing their legal situation.

Once compiled, this program will statically link against the third-party
Cargo crates "lazy_static" and "libc". These are dual-licensed under the
MIT license and the Apache 2.0 license. Copies of the licenses for these
crates can be found in the "licenses/thirdparty/crates" directory.

Compiled forms of this program may statically or dynamically link against
other libraries. All those libraries are available under their respective
licenses.
07070100000002000081A4000000000000000000000001647DB23000000243000000000000000000000000000000000000001B00000000evsieve-1.4.0~0/Cargo.lock# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "evsieve"
version = "1.4.0"
dependencies = [
 "lazy_static",
 "libc",
]

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "libc"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
07070100000003000081A4000000000000000000000001647DB2300000065A000000000000000000000000000000000000001B00000000evsieve-1.4.0~0/Cargo.toml[package]
name = "evsieve"
version = "1.4.0"
authors = ["Kars Mulder <devmail@karsmulder.nl>"]
edition = "2018"
keywords = ["evdev"]
categories = ["command-line-utilities"]
links = "evdev"
rust-version = "1.59"

# The following "license = " key includes licenses from some dependencies, even if no code
# from those dependencies is on this repository. See the COPYING file for more detailed information.
#
# Most of this program is (compatible with) GPL-2.0-or-later, but I'm not a lawyer and
# I am unable to determine whether or not this program is legally a derivative work of the
# Linux kernel, so I'm marking the license conservatively in Cargo.toml. If you wish to
# redistribute this program under GPLv3+, you should do your own legal research on whether
# that is possible.
license = "GPL-2.0-or-later AND MIT AND GPL-2.0-only WITH Linux-syscall-note"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
lazy_static = "1.4.0"
libc = "0.2.82"

[profile.release]
panic = "unwind"
lto = true

# Warning: optional features are experimental and not subject to stability guarantees,
# i.e. they may work different or not be available on future evsieve versions.
[features]
# If enabled, evsieve will link against libsystemd. If not, it will fall back on systemd-notify.
systemd = []
# If enabled, evsieve will accept the --control-fifo command line argument.
control-fifo = []
#If enabled, evsieve will accept the --config command line argument.
config = []
# If enabled, evsieve will partially autogenerate EV_MSC events sent to output devices.
auto-scan = []
07070100000004000081A4000000000000000000000001647DB230000046AC000000000000000000000000000000000000001800000000evsieve-1.4.0~0/LICENSE                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
07070100000005000081A4000000000000000000000001647DB23000012890000000000000000000000000000000000000001A00000000evsieve-1.4.0~0/README.md# What is evsieve?

Evsieve (from "event sieve") is a low-level utility that can read events from Linux event devices ([evdev](https://en.wikipedia.org/wiki/Evdev)) and write them to virtual event devices ([uinput](https://www.kernel.org/doc/html/latest/input/uinput.html)), performing simple manipulations on the events along the way. Examples of things evsieve can do are:

* Remap certain keyboard keys to others;
* Send some keyboard keys to one virtual device and other keyboard keys to another virtual device;
* Turn a joystick into a virtual keyboard.

Evsieve is particularly intended to be used in conjunction with the evdev-passthrough functionality of [Qemu](https://www.qemu.org/). For other purposes, you may be better off using a higher-level utility than evsieve.

Examples of things that evsieve can achieve that are useful for evdev-passthrough are:

* Keeping certain keys on the host while other keys are passed through to the guest;
* Run scripts based on hotkeys even if your keyboard is passed through to the guest;
* Remap another key to lctrl+rctrl, so you can use another key to attach/detach your keyboard to your VM;
* Split the events over multiple virtual devices, possibly passing some of those devices to different VMs or keeping one of them on the host;
* Remap keys on your keyboard on such a low level that even the guest OS can't tell the difference between real and mapped keys.

Evsieve is intended to make simple manipulations easy. It is **not** a tool for keyboard macro's or complex event manipulation. In case you want to do manipulations that are beyond the scope of evsieve, we recommend using the [Python-evdev](https://python-evdev.readthedocs.io/en/latest/) library instead.

# Compilation

Compiling this program requires the Rust toolchain and the libevdev library. To install these dependencies:

* **Ubuntu:** `sudo apt install cargo libevdev2 libevdev-dev`
* **Debian 12 (Bookworm):** `sudo apt install cargo libevdev2 libevdev-dev`
* **Debian 11 (Bullseye):** `sudo apt install rustc-mozilla cargo libevdev2 libevdev-dev`
* **Fedora:** `sudo dnf install cargo libevdev libevdev-devel`
* **Arch Linux:** `sudo pacman -S rust libevdev`

After you've installed the dependencies, you can obtain a copy of the source code and compile the program using:

```
wget https://github.com/KarsMulder/evsieve/archive/v1.4.0.tar.gz -O evsieve-1.4.0.tar.gz
tar -xzf evsieve-1.4.0.tar.gz && cd evsieve-1.4.0
cargo build --release
```

An executable binary can then be found in `target/release/evsieve`. You can either execute it as-is, or copy it to somewhere in your PATH for your convenience, e.g.:

```
sudo install -m 755 -t /usr/local/bin target/release/evsieve
```

The source code repository contains some files which were automatically generated. You can optionally regenerate these files before compilation. To regenerate these files, run the `generate_bindings.sh` script before running `cargo build`. This script requires the `rust-bindgen` tool to be installed on your system.

# Usage: Basic concepts

Linux event devices are a tool the kernel uses to inform userspace applications (such as Xorg, Wayland) about input events such as keyboard presses, mouse movements. Event devices reside in `/dev/input` and usually have named symlinks pointed to them in `/dev/input/by-id`.

Event devices emit a stream of events. For example, when you press the A key on your key board, it will emit an (EV_KEY, KEY_A, 1) event, and when you release it it will emit an (EV_KEY, KEY_A, 0) event.

The quickest way to get an idea of how it works is to just look at it. You can either install the utility `evtest` (shipped by most major Linux distributions), run it as `sudo evtest`, select your keyboard/mouse/whatever and look at the events it emits, or run evsieve with the following arguments to see all events that are emitted on your computer:

```
sudo evsieve --input /dev/input/event* --print
```

## Reading and writing

Evsieve is a command-line program designed to read and transform input events emitted by (real) event devices and write them to virtual event devices. It most likely needs to run as root for most features.

Assume you have a keyboard, and a symlink to its event device can be found at `/dev/input/by-id/keyboard`. A very basic usage of evsieve would be the following:

```
evsieve --input /dev/input/by-id/keyboard grab --output
```

In this example, evsieve will open the event device `/dev/input/by-id/keyboard` for exclusive read access (specified by the `grab` flag), read events from it, and write those same events to an unnamed virtual event device. This changes the flow of events from:

* Keyboard → Kernel → Event Device → Xorg/Wayland → Applications

To:

* Keyboard → Kernel → Event Device → Evsieve → Virtual Event Device → Xorg/Wayland → Applications

This has effectively accomplished nothing besides adding about 0.15ms of latency. However, if we add additional arguments to evsieve, we can use this construction to add, change, or suppress events. For example, the following script will turn the capslock into a second backspace:

```
evsieve --input /dev/input/by-id/keyboard grab \
        --map key:capslock key:backspace \
        --output
```

Since evsieve has opened your keyboard `/dev/input/by-id/keyboard` with exclusive access due to the `grab` flag, the userspace programs won't notice when you press capslock on your real keyboard. Evsieve will then read this capslock event, turn it into a backspace key event and write that to the virtual device, tricking userspace into thinking you just pressed the backspace button instead of the capslock button.

Evsieve actively maps events from the input devices to the output devices as long as evsieve runs. When evsieve exits, the virtual devices will be removed and any grabbed input devices will be released. You can exit evsieve by sending it a SIGINT or SIGTERM signal, for example by pressing Ctrl+C in the terminal where evsieve runs.

Take note that evsieve deals with events, not keys. This doesn't turn the capslock key into a backspace key, but rather turns every event associated with capslock into an event associated with backspace, e.g. a key_down for capslock is turned into a key_down for backspace, and a key_up event for capslock is turned into a key_up event for backspace. This distinction is important for some of the more advanced uses of evsieve.

## Sequential event processing

The order of the arguments provided to evsieve matters. This is similar to how [ImageMagick](https://imagemagick.org/) works and unlike the POSIX convention.

The `--input` argument reads events from the disk and generates a stream of events that will be processed by the arguments that come after. Each argument modifies the stream of events generated by previous argument. The `--map` arguments modify the stream of events by turning some events into other events. The `--output` argument consumes the events and writes them to a virtual event device.

In bash terms, you could think of all evsieve's arguments as if they were mini programs which have their outputs piped to each other. The previous script can be thought of as if it were something like the following:

```
# Note: this is not an actually valid evsieve script.
evsieve-input --grab /dev/input/by-id/keyboard | evsieve-map key:capslock key:backspace | evsieve-output
```

Due to performance concerns and some technical reasons, evsieve is not structured as a set of mini-programs but rather as a single monolithic process, but the idea is the same.

# Usage: Examples

In this section we'll introduce the capabilities of evsieve through example scripts. All examples will assume that your keyboard can be found at `/dev/input/by-id/keyboard` and your mouse can be found at `/dev/input/by-id/mouse`. If you want to use these scripts, you need to replace these placeholder paths with the real paths to your keyboard and mouse.

All these scripts need to run with a certain amount of privileges. That usually means you'll need to execute them as `root` using `sudo`. If you want to put effort in making it run with more minimal permissions using ACLs, it is sufficient for most purposes to have evsieve run as a user with read/write access to `/dev/uinput` and all devices you try to open.

## See which events are being emitted by your keyboard

```
evsieve --input /dev/input/by-id/keyboard --print
```

You can insert a `--print` argument at any point in your script to see which events are being processed by evsieve at that point. The events are printed the way they are seen by evsieve at that point; if you applied some maps or other transformations before the `--print` statement, then the events printed are the mapped events, not the original ones.

This is useful for debugging your scripts, and is also useful if you want to know what a certain key or button is called by evsieve. You can alternatively use the standard utility `evtest` (shipped by most major Linux distributions), which will provide a bit more detailed information using the Linux kernel's standard terminology.

## Execute a script when some hotkey is pressed

```
evsieve --input /dev/input/by-id/keyboard \
        --hook key:leftctrl key:h exec-shell="echo Hello, world!"
```

You can execute a certain script when a certain key or combination of keys are pressed simultaneously using the `--hook` argument.

Each time the left control and the H key are pressed simultaneously, evsieve will execute the command `echo Hello, world!` using your OS' default POSIX shell, i.e. `/bin/sh -c "echo Hello, world!"`. Note that the script will be executed as the same user as evsieve runs as, which may be `root`.

Since we did not need to modify the events and merely read them, there is no `grab` flag on the input device, causing evsieve to open it in nonexclusive mode. Since we didn't claim exclusive access to the device, there is no need to generate a virtual device using `--output` either.

In case some other program (such as Qemu) claims exclusive access to your keyboard and you still want to be able to listen to hotkeys, you can generate a virtual device and let Qemu claim access to the virtual device instead:

```
evsieve --input /dev/input/by-id/keyboard grab \
        --hook key:leftctrl key:h exec-shell="echo Hello, world!" \
        --output create-link=/dev/input/by-id/virtual-keyboard
```

This will generate a virtual device and create a link to it at `/dev/input/by-id/virtual-keyboard`, which you can pass to Qemu or whatever program demands exclusive access.

## Remap your favourite key to lctrl+rctrl

```
evsieve --input /dev/input/by-id/keyboard grab \
        --map key:scrolllock key:leftctrl key:rightctrl \
        --output create-link=/dev/input/by-id/virtual-keyboard
```

In case you want to use Qemu evdev passthrough and use some other key than lctrl+rctrl to switch between guest and host, this is the second-easiest solution. (The easiest solution, of course, is using Qemu's [built-in functionality](https://patchwork.kernel.org/project/qemu-devel/patch/20190124103651.26227-2-kraxel@redhat.com/) for this purpose.)

## Swap the control and the shift keys

```
evsieve --input /dev/input/by-id/keyboard grab    \
        --map yield key:leftctrl   key:leftshift  \
        --map yield key:leftshift  key:leftctrl   \
        --map yield key:rightctrl  key:rightshift \
        --map yield key:rightshift key:rightctrl  \
        --output
```

In previous examples, we mapped a single key using `--map`. In this example we do the same, except this time we also pass the `yield` flag to the `--map` arguments.

Remember that the output events of one argument are input events for the next argument. The `--input` argument emits a constant stream of events that are processed by all the maps until they encounter an `--output` event.

A first map like `--map key:leftctrl key:leftshift` could turn a control key event into a shift key event without problem. However, if the next argument were to naively be `--map key:leftshift key:leftctrl`, then the second map would turn the shift key event back into a control key—undoing the effect of the first map.

There are use cases where this behaviour is useful. For cases where it isn't, the `--map` argument accepts a `yield` flag. All events that are generated by a `--map yield` are considered "yielded" and will not trigger any further maps, nor any other arguments except for the `--output` argument. This prevents the shift event generated by the first map from getting turned back to a ctrl event by later maps.

## Process two devices simultaneously

```
evsieve --input /dev/input/by-id/keyboard grab \
        --output                               \
        --input /dev/input/by-id/mouse grab    \
        --output
```

Evsieve is not restricted to a single input or output, and is also not restricted to only keyboards.

This results into two virtual devices, one virtual device for the keyboard event (created by the first `--output`) and a virtual device for all mouse events (created by the second `--output`). The keyboard arguments are not sent to the virtual mouse because no event in the event processing stream gets past the first `--output` argument.

It is also possible to map both the keyboard and the mouse to a single virtual hybrid key-mouse device in the following way.:

```
evsieve --input /dev/input/by-id/keyboard grab \
        --input /dev/input/by-id/mouse grab    \
        --output
```

## Keeping certain keyboard keys on the host after passing a keyboard to the guest

```
evsieve --input /dev/input/by-id/keyboard grab \
        --output key:f9 key:f10 key:f11 key:f12 \
        --output create-link=/dev/input/by-id/virtual-keyboard
```

This example is useful for those who wish to do evdev passthrough using Qemu.

It is possible to specify event filters on `--output` arguments. By default an `--output` argument will take all events and write them to an output device, but if one or more filters are specified, then only events that match at least one of those filters will be written to that output device.

In this example, the first `--output` argument creates an unnamed virtual device, and writes all events related to the F9–F12 keys to it. Those events are then removed from the processing stream.

All events that were not removed by the first `--output` argument encounter the second `--output` argument, which applies to all events and sends them to a second virtual device which is linked to at `/dev/input/by-id/virtual-keyboard`.

You can then pass the latter device `/dev/input/by-id/virtual-keyboard` through to your virtual machine using evdev passthrough and keep the former unnamed device on the host. Now your host will still be able to receive the F9–F12 keys from the real keyboard while the majority of your keys are send to your VM through the latter keyboard.

## Toggle events between two virtual devices

```
evsieve --input  /dev/input/by-id/keyboard domain=kb grab \
        --input  /dev/input/by-id/mouse    domain=ms grab \
        --hook   key:scrolllock toggle   \
        --map    key:scrolllock @host-kb \
        --toggle @kb @guest-kb @host-kb  \
        --toggle @ms @guest-ms @host-ms  \
        --output @host-kb create-link=/dev/input/by-id/host-keyboard   \
        --output @host-ms create-link=/dev/input/by-id/host-mouse      \
        --output @guest-kb create-link=/dev/input/by-id/guest-keyboard \
        --output @guest-ms create-link=/dev/input/by-id/guest-mouse
```

A somewhat more involved solution that's primarily useful for switching your input between the host and guest when doing evdev passthrough. This script reads your real keyboard and mouse, and creates two virtual devices for each of them.

By default, all events will be written to the `guest-*` devices, but by pressing the scrolllock key, you can change that to send write all of them to the `host-*` devices instead. Pressing scrolllock again with switch them back to the `guest-*` devices.

If you then pass the `guest-*` devices through to your virtual machine and leave the `host-*` devices on your host, then you can switch your input between your host and guest by pressing the scrolllock key. The scrolllock key is always sent to the host using `--map` so the LED indicator works properly.

You could also do something like passing both of the devices through to two different virtual machines, so you can toggle between both of the machines instead of toggling between host and guest. Of course, it is also possible to create more than two virtual devices per real device: just add more `@something` domains to the `--toggle`s along with corresponding `--output`s.

**Explanation**

Two new features are introduced in this script: the `--toggle` argument, and the concept of domains. A `toggle` is like a map, except that only one of the output keys is active at a time, and the active output key is switched every time a `--hook toggle` is triggered. An argument like `--toggle key:a key:b key:c` will by default map all A key events to B key events, but when a `--hook toggle` is triggered, it will start mapping them to C key events instead. Triggering a `--hook toggle` again will make the toggle go back to turning them into B key events.

In this script, we do not match events by type or code such as `key:a`, but instead by domains. In evsieve logic, all events have a domain attached to them. A domain is a string of text without any intrinsic meaning; they are not something that exists in the evdev protocol, but they are useful for writing evsieve scripts. Because the `domain=kb` clause was set on an input device, all events read from that device will have the domain `kb` attached to it.

A filter like `@kb` matches all events which have domain `kb`, just like a filter `key:a` matches all events with type EV_KEY and code KEY_A. The argument `--toggle @kb @guest-kb @host-kb` will take all events with domain `kb` and change their domain to `guest-kb`. When the `--hook toggle` is triggered, it will start changing their domain to `host-kb` instead.

Next, the output devices also filter by domain. There is one virtual output which will write all events with domain `guest-kb` to it, and another output which will write all events with domain `host-kb` to it. Thus, by changing the domains of events, this `--toggle` effectively decides to which virtual device the events will be written.

**Advanced variations**

In case you want a set/reset behaviour instead of toggle behaviour, i.e. one key that always switches to host and one key that always switches to guest, you can specify an index for the toggle, for example:

```
evsieve --input  /dev/input/by-id/keyboard domain=kb grab \
        --input  /dev/input/by-id/mouse    domain=ms grab \
        --hook   key:kpenter toggle=:1  \
        --hook   key:kpdot   toggle=:2  \
        --block  key:kpenter key:kpdot  \
        --toggle @kb @guest-kb @host-kb \
        --toggle @ms @guest-ms @host-ms \
        --output @host-kb create-link=/dev/input/by-id/host-keyboard   \
        --output @host-ms create-link=/dev/input/by-id/host-mouse      \
        --output @guest-kb create-link=/dev/input/by-id/guest-keyboard \
        --output @guest-ms create-link=/dev/input/by-id/guest-mouse
```

This uses the numpad's enter key to switch to the guest and the numpad's dot key to switch to the host. The `--block` argument drops all events matching one of the keys specified, so the numpad enter/dot are sent to neither host or guest.

You can go further and specify some `exec=shell=` clause on the hooks to execute a certain script whenever the input target changes. For example, if your monitor supports the DDC2 protocol, you may be able to use the `ddcutil` utility to change its input source every time you switch between host and guest:

```
evsieve --input  /dev/input/by-id/keyboard domain=kb grab \
        --input  /dev/input/by-id/mouse    domain=ms grab \
        --hook   key:kpenter toggle=:1 exec-shell="ddcutil setvcp 60 0x11" \
        --hook   key:kpdot   toggle=:2 exec-shell="ddcutil setvcp 60 0x0f" \
        --block  key:kpenter key:kpdot  \
        --toggle @kb @guest-kb @host-kb \
        --toggle @ms @guest-ms @host-ms \
        --output @host-kb create-link=/dev/input/by-id/host-keyboard   \
        --output @host-ms create-link=/dev/input/by-id/host-mouse      \
        --output @guest-kb create-link=/dev/input/by-id/guest-keyboard \
        --output @guest-ms create-link=/dev/input/by-id/guest-mouse
```

You may need to adjust the numbers involved depending on your monitor. Not all monitors have proper support for the DDC2 protocol and/or may refuse to listen to commands that do not come from its active input. Read the `ddcutil` manual for more information.

## Control your mouse using your keyboard

```
evsieve --input /dev/input/by-id/keyboard \
        --input /dev/input/by-id/mouse grab domain=mouse \
        --map key:left:1~2      rel:x:-20@mouse \
        --map key:right:1~2     rel:x:20@mouse  \
        --map key:up:1~2        rel:y:-20@mouse \
        --map key:down:1~2      rel:y:20@mouse  \
        --map key:enter:0~1     btn:left@mouse  \
        --map key:backslash:0~1 btn:right@mouse \
        --output @mouse
```

This script will create a virtual mouse that moves with every key_down or key_repeat event on the arrow keys, and can left/right click with the enter/backslash keys. Keyboard to mouse mapping is not what evsieve was intended to be used for, but it works anyway to some extent.

A new feature in this script is value mapping. In previous scripts, when we saw an argument like `--map key:a key:b` it meant "turn any event of type EV_KEY, code KEY_A, any value, into an event of type EV_KEY, code KEY_B, with the same value". In this script, we're not only mapping the event type and code, but also the value.

The key `rel:x:-20` means "an event with type EV_REL, code REL_X, value -20". The key `key:left:1~2` means "an event with type EV_KEY, code KEY_LEFT, value between 1 and 2", where the `~` denotes a range. Thus the map `--map key:left:1~2 rel:x:-20` maps any event of type `EV_KEY`, code `KEY_LEFT`, value 1 or 2 to an event of type EV_REL, code REL_X, value -20. This effectively makes any key down (value 1) or key repeat (value 2) event on the left arrow key change the mouse's X axis with -20.

In the later map `--map key:enter:0~1 btn:left` you may notice that no value is specified for the output event. This means that it's value is taken to be the same as the input event, which conveniently maps a key down event (value 1) to a click event, and a key up event (value 0) to a release event.

Finally, all events with the domain `mouse` are written to a virtual device. This are all events generated by the real mouse and all events that were created by a map. Events generated by your real keyboard that didn't match any map will not be written to this virtual device. Note that there is no `grab` flag provided to the keyboard input, so evsieve will open the keyboard in nonexclusive mode, allowing other programs to read from it as well.

## Map a controller to a virtual keyboard

If an application does not support controllers or joysticks but you want to control them with a controller or joystick anyway, you can use evsieve to turn your controller/joystick into a virtual keyboard.

Controllers are seen as joysticks by the Linux kernel. The good news is that they have event devices just like keyboards and can be processed by evsieve. The bad news is that the buttons on them do not tend to have sensible names or event codes, so the commands you need to map them tend to be cryptic and dependent on what controller you use.

Below are example scripts that create a virtual keyboard based on a DualSense or Xbox One controller. You can change which keyboard keys the controller buttons get mapped to as you please.

If you want to write a mapping script for some other controller or joystick, we recommend using the `--print` argument or the Linux utility `evtest` to discover which event codes are associated with the buttons on your controller.

**DualSense controller:**

```
evsieve --input /dev/input/by-id/usb-Sony_Interactive_Entertainment_Wireless_Controller-if03-event-joystick \
                                                \
        `# Directional pad`                     \
        --copy abs:hat0x:-1      key:left:1@kb  \
        --copy abs:hat0x:-1..0~  key:left:0@kb  \
        --copy abs:hat0x:1       key:right:1@kb \
        --copy abs:hat0x:1..~0   key:right:0@kb \
        --copy abs:hat0y:-1      key:up:1@kb    \
        --copy abs:hat0y:-1..0~  key:up:0@kb    \
        --copy abs:hat0y:1       key:down:1@kb  \
        --copy abs:hat0y:1..~0   key:down:0@kb  \
                                                \
        `# Shape buttons: circle, cross, square, triangle` \
        --copy btn:c             key:z@kb       \
        --copy btn:east          key:x@kb       \
        --copy btn:south         key:c@kb       \
        --copy btn:north         key:v@kb       \
                                                \
        `# Left pad`                            \
        --copy abs:z:38~..~37    key:j:1@kb     \
        --copy abs:z:~37..38~    key:j:0@kb     \
        --copy abs:z:~216..217~  key:l:1@kb     \
        --copy abs:z:217~..~216  key:l:0@kb     \
        --copy abs:rz:38~..~37   key:i:1@kb     \
        --copy abs:rz:~37..38~   key:i:0@kb     \
        --copy abs:rz:~216..217~ key:k:1@kb     \
        --copy abs:rz:217~..~216 key:k:0@kb     \
                                                \
        `# Right pad`                           \
        --copy abs:x:38~..~37    key:a:1@kb     \
        --copy abs:x:~37..38~    key:a:0@kb     \
        --copy abs:x:~216..217~  key:d:1@kb     \
        --copy abs:x:217~..~216  key:d:0@kb     \
        --copy abs:y:38~..~37    key:w:1@kb     \
        --copy abs:y:~37..38~    key:w:0@kb     \
        --copy abs:y:~216..217~  key:s:1@kb     \
        --copy abs:y:217~..~216  key:s:0@kb     \
                                                \
        `# Press left pad, press right pad`     \
        --copy btn:select        key:t@kb       \
        --copy btn:start         key:g@kb       \
                                                \
        `# Back buttons: LB, RB, LT, RT`        \
        --copy btn:west         key:q@kb        \
        --copy btn:z            key:e@kb        \
        --copy abs:rx:~50..51~  key:u:1@kb      \
        --copy abs:rx:51~..~50  key:u:0@kb      \
        --copy abs:ry:~50..51~  key:o:1@kb      \
        --copy abs:ry:51~..~50  key:o:0@kb      \
                                                \
        `# Special buttons: create, start, PlayStation, touch pad, microphone` \
        --copy btn:tl2           key:y@kb       \
        --copy btn:tr2           key:enter@kb   \
        --copy btn:mode          key:esc@kb     \
        --copy btn:thumbl        key:n@kb       \
        --copy btn:thumbr        key:m@kb       \
                                                \
        --output @kb repeat
```

**Xbox One controller:**

```
evsieve --input /dev/input/by-id/usb-Microsoft_Controller_*-event-joystick \
                                                      \
        `# Directional pad`                           \
        --copy abs:hat0y:-1            key:up:1@kb    \
        --copy abs:hat0y:-1..0~        key:up:0@kb    \
        --copy abs:hat0y:1             key:down:1@kb  \
        --copy abs:hat0y:1..~0         key:down:0@kb  \
        --copy abs:hat0x:-1            key:left:1@kb  \
        --copy abs:hat0x:-1..0~        key:left:0@kb  \
        --copy abs:hat0x:1             key:right:1@kb \
        --copy abs:hat0x:1..~0         key:right:0@kb \
                                                      \
        `# Letter buttons: A, B, X, Y`                \
        --copy btn:south               key:z@kb       \
        --copy btn:east                key:x@kb       \
        --copy btn:north               key:c@kb       \
        --copy btn:west                key:v@kb       \
                                                      \
        `# Left pad`                                  \
        --copy abs:x:~23169..23170~    key:d:1@kb     \
        --copy abs:x:23170~..~23169    key:d:0@kb     \
        --copy abs:x:-23169~..~-23170  key:a:1@kb     \
        --copy abs:x:~-23170..-23169~  key:a:0@kb     \
        --copy abs:y:~23169..23170~    key:s:1@kb     \
        --copy abs:y:23170~..~23169    key:s:0@kb     \
        --copy abs:y:-23169~..~-23170  key:w:1@kb     \
        --copy abs:y:~-23170..-23169~  key:w:0@kb     \
                                                      \
        `# Right pad`                                 \
        --copy abs:rx:~23169..23170~   key:l:1@kb     \
        --copy abs:rx:23170~..~23169   key:l:0@kb     \
        --copy abs:rx:-23169~..~-23170 key:j:1@kb     \
        --copy abs:rx:~-23170..-23169~ key:j:0@kb     \
        --copy abs:ry:~23169..23170~   key:k:1@kb     \
        --copy abs:ry:23170~..~23169   key:k:0@kb     \
        --copy abs:ry:-23169~..~-23170 key:i:1@kb     \
        --copy abs:ry:~-23170..-23169~ key:i:0@kb     \
                                                      \
        `# Press left pad, press right pad`           \
        --copy btn:thumbl              key:t@kb       \
        --copy btn:thumbr              key:g@kb       \
                                                      \
        `# Back buttons: LB, RB, LT, RT`              \
        --copy btn:tl                  key:q@kb       \
        --copy btn:tr                  key:e@kb       \
        --copy abs:z:~203..204~        key:u:1@kb     \
        --copy abs:z:204~..~203        key:u:0@kb     \
        --copy abs:rz:~203..204~       key:o:1@kb     \
        --copy abs:rz:204~..~203       key:o:0@kb     \
                                                      \
        `# Special buttons: start, select, Xbox button` \
        --copy btn:start               key:enter@kb   \
        --copy btn:select              key:space@kb   \
        --copy btn:mode                key:esc@kb     \
                                                      \
        --output @kb repeat
```

The major new feature used in these scripts are transitions: a transition like `~216..217~` matches an event with a value of 217 or greater, provided that the last event with the same type and code had a value of 216 or less. These transitions are useful for detecting when an absolute axis event goes over or under a certain threshold.

The chosen thresholds determine how far you need to move a stick or trigger to make them cause a keyboard event. You can adjust them to make the maps more or less sensitive to small movements.

You may further notice that these scripts use the `--copy` argument instead of `--map`. The `--copy` and `--map` arguments are identical with one difference: source events matching a `--map` are removed from the processing stream, but those matching `--copy` are not. In these scripts `--copy` is used because it might be theoretically possible that e.g. a `abs:hat0x:-1` event gets followed up by a `abs:hat0x:1` event, which should simultaneously release the left key and press the right key.

The `repeat` flag makes the virtual keyboard emit key repeat events if a button is held down, like a keyboard does, even though the controllers do not emit repeat events themselves. Since most applications that read event devices ignore repeat events anyway, this clause isn't very important.

## Change your keyboard layout on an evdev level

It is probably better to change your keyboard layout by changing your OS settings, but if that isn't possible for some reason, it is possible to use evsieve to map every key to its corresponding key on your favourite keyboard layout. For example, the following script will map the Qwerty keys to [Colemak](https://colemak.com/):

```
evsieve --input /dev/input/by-id/keyboard grab \
        --map yield key:e key:f \
        --map yield key:r key:p \
        --map yield key:t key:g \
        --map yield key:y key:j \
        --map yield key:u key:l \
        --map yield key:i key:u \
        --map yield key:o key:y \
        --map yield key:s key:r \
        --map yield key:d key:s \
        --map yield key:f key:t \
        --map yield key:g key:d \
        --map yield key:j key:n \
        --map yield key:k key:e \
        --map yield key:l key:i \
        --map yield key:n key:k \
        --map yield key:p key:semicolon \
        --map yield key:semicolon key:o \
        --map yield key:capslock key:backspace \
        --output
```

## Change how keys are mapped at runtime

Similar to how domains and toggles can be used to decide to which output device events are written, they can also be used to determine whether maps apply to events. For example, the following script will toggle between using the default keyboard layout (assumed to be Qwerty) and the Colemak layout by pressing lctrl+rctrl:

```
evsieve --input /dev/input/by-id/keyboard grab \
        --hook key:leftctrl key:rightctrl toggle \
        --toggle "" @qwerty @colemak \
        --map yield key:e@colemak key:f \
        --map yield key:r@colemak key:p \
        --map yield key:t@colemak key:g \
        --map yield key:y@colemak key:j \
        --map yield key:u@colemak key:l \
        --map yield key:i@colemak key:u \
        --map yield key:o@colemak key:y \
        --map yield key:s@colemak key:r \
        --map yield key:d@colemak key:s \
        --map yield key:f@colemak key:t \
        --map yield key:g@colemak key:d \
        --map yield key:j@colemak key:n \
        --map yield key:k@colemak key:e \
        --map yield key:l@colemak key:i \
        --map yield key:n@colemak key:k \
        --map yield key:p@colemak key:semicolon \
        --map yield key:semicolon@colemak key:o \
        --map yield key:capslock@colemak key:backspace \
        --output
```

Based on the state of the toggle, all events either get tagged with the domain `qwerty` or `colemak`. The further maps then only apply to events that are tagged with the domain `colemak`. No matter which domain the events belong to, they get written to the same output device.

## Running evsieve as a systemd service

Since version 1.3, evsieve has optional systemd integration, specifically if evsieve is ran as a systemd service with service type "notify", evsieve will notify systemd when it has created all virtual output devices and is ready to start listening for input events.

The `systemd-run` command makes evsieve easy to integrate in shell scripts where you want to do something with the virtual output devices, for example:

```
systemd-run --service-type=notify --unit=virtual-keyboard.service evsieve \
        --input /dev/input/by-id/keyboard \
        --output create-link=/dev/input/by-id/virtual-keyboard

# After systemd-run returns successfully, /dev/input/by-id/virtual-keyboard exists. You can
# immediately run any scripts that depend on it existing. There is no need for any kind of
# `sleep` call here.

# Example: pass the virtual device to Qemu and wait until Qemu exits.
qemu-system-x86_64 \
    `# More arguments here` \
    -object input-linux,id=virtual-keyboard,evdev=/dev/input/by-id/virtual-keyboard

# Finally, after the virtual devices are no longer needed, you can stop evsieve like this.
systemctl stop virtual-keyboard.service
```

**Micro-tutorial: error handling with `systemd-run`:**

If evsieve exits with an error status (e.g. invalid argument or input device not available) then the unit name (in the above example, `virtual-keyboard.service`) remains in use. When you try to run the same script again, you will get the following error message:

```
Failed to start transient service unit: Unit virtual-keyboard.service already exists.
```

This is a double edged sword: on one hand, this makes it easy to find which part of your shell script broke and allows you to check the status of `virtual-keyboard.service` to see what went wrong. To check the status and error messages, use `systemctl status virtual-keyboard.service` or `journalctl -u virtual-keyboard.service`.

On the other hand, it prevents you from just rerunning the same script without manual intervention. If you get the above error message, you need to run `systemctl reset-failed virtual-keyboard.service` to make that unit name available again. Alternatively, you can pass the `--collect` flag to `systemd-run` to make sure the unit name stays available even if `evsieve` exits with error status.

**Caveats and known issues:**

When using a SELinux-enabled operating system such as Fedora, systemd may fail to execute the `evsieve` binary unless it is located in one of the standard executable directories such as `/usr/local/bin`.

When evsieve is not ran as root, evsieve may be unable to notify the systemd daemon that it is ready unless the property `NotifyAccess=all` is set. When running evsieve using `systemd-run`, this property can be set like `systemd-run --service-type=notify --property=NotifyAccess=all evsieve`.

# Usage: In detail

In this section, we'll provide comprehensive documentation about all the arguments evsieve accepts.

## Maps

The `--map` and `--copy` arguments have the following basic syntax:

```
    --map  SOURCE_EVENT [TARGET_EVENT...] [yield]
    --copy SOURCE_EVENT [TARGET_EVENT...] [yield]
```

A map triggers every time an event matching the SOURCE_EVENT is generated by a physical device, and then generates event(s) matching all TARGET_EVENT(s) specified.

When a source event triggers a `--map`, it is removed from the processing stream and will not trigger any further arguments. The `--copy` argument has the same syntax and semantics as `--map` with the single difference being that source events that trigger `--copy` are *not* removed from the processing stream.

If a target event is not fully specified (e.g. it specifies a keycode but not a value), all missing details of the target event shall be taken to be the same as those of the source event. If zero target events are specified on a `--map`, then the source event is removed from the processing stream and no new events are added.

**Key format**

Keys can be provided to the `--map` arguments. They use the same form for both the input event and output events. They have the form

```
    event_type:event_code:event_value@domain
```

For more information about the "@domain" part, see the "Domains" section below. We will first talk about the type, code, and value.

Most parts are optional. Any part that is not specified will be interpreted to mean "any" for source events. For example:

```
    ""              # The empty string matches any event
    key:a           # Matches any event with type EV_KEY, code KEY_A
    key:a:1         # Matches any event with type EV_KEY, code KEY_A, value 1 (down)
    key:a@keyboard  # Matches any event with type EV_KEY, code KEY_A with domain "keyboard"
    @keyboard       # Matches any event with domain "keyboard"   
    key::1          # Matches any event with type EV_KEY, code KEY_(something), value 1 (down)
    ::1             # Matches any event with value 1.
```

For output events, any part that is not specified will be interpreted as "same as the source event". For example:

```
    --map key:a   key:b    # Will map any (EV_KEY, KEY_A) event to a (EV_KEY, KEY_B) event with the same event value.
    --map key:a:1 key:b:0  # Will map any (EV_KEY, KEY_A, value=1) event to a (EV_KEY, KEY_B, value=0) event.
    --map key:a   ""       # Will map any (EV_KEY, KEY_A) event to an identical event.
    --map abs:x   ::0      # Will map any (EV_ABS, ABS_X) event to an (EV_ABS, ABS_X, value=0) event.
```

The value for input events may also be expressed as an (optionally bounded) range. For example:

```
    key:a:1~2        # Matches any event with type EV_KEY, code KEY_A, a value of 1 (down) or 2 (repeat)
    abs:x:10~50      # Matches any event with type EV_ABS, code ABS_X, a value between 10 and 50
    abs:x:51~        # Matches any event with type EV_ABS, code ABS_X, a value of 51 or higher
```

Furthermore, when matching an input event, it is possible to specify a transition of values. If a transition is specified, then a certain event only matches if its value lies in the specified range and the value of the previous event with the same type and code from the same input device also lies within a certain range. For example:

```
    key:a:1..2       # Matches an A key event with value 2 (repeat),
                     # if the last A key event had value 1 (down).
    abs:x:~50..51~   # Matches an X axis event with a value of 51 or greater,
                     # provided that the previous X axis value was 50 or less.
```

Ranges and transitions are particularly useful for mapping absolute events to key events. For example, the following maps will press the A key when the X axis goes over 200, and releases the key when it goes under it:

```
    --map abs:x:~199..200~ key:a:1
    --map abs:x:200~..~199 key:a:0
```

For output events, ranges and transitions cannot be used. However, there are two special variables `x` and `d` that can be used to make the value of the mapped event dependent on the value of the input event. The variable `x` represents the value of the source event, and can be used for example in the following ways:

```
    --map rel:y rel:y:0.5x   # Halves the value of all rel:y events.
    --map rel:y rel:y:-x     # Inverts the y axis.
    --map abs:y abs:y:255-x  # If the original range of the abs:y events was [0..255],
                             # this inverts the y axis.
```

The special variable `d` represents the difference between the source event's value and the value of the previous event with the same type and code generated by the same input device. This is mainly useful for mapping `abs` events to `rel` events. For example:

```
    # If an abs:x:10 event gets followed up by a abs:x:50 event, then the latter one gets mapped to rel:x:40.
    --map abs:x rel:x:d

    # If an abs:y:10 event gets followed up by a abs:y:50 event, then the latter one gets mapped to rel:y:8.
    --map abs:y rel:y:0.2d
```

**Key names**

All names that evsieve uses for events are derived from the names used by the Linux kernel for such events, using a fairly systematic way that's understood most quickly by looking at the following examples:

```
    EV_KEY, KEY_A -> key:a
    EV_REL, REL_X -> rel:x
    EV_ABS, ABS_X -> abs:x
    EV_KEY, KEY_LEFTCTRL -> key:leftctrl
    EV_REL, REL_WHEEL_HI_RES -> rel:wheel_hi_res
```

In case you're wondering what the kernel calls a certain button on your device, we recommend using the third-party program `evtest`, which is probably shipped by your favourite distribution.

There is one notable exception to the above scheme. There are events with type EV_KEY and a code of form BTN_*. These are called btn:something by evsieve even though though there is no EV_BTN event type. For example:

```
    EV_KEY, BTN_RIGHT -> btn:right
    EV_KEY, BTN_NORTH -> btn:north
```

You can also specify an event code by its numeric value, which is useful in case your device emits an event with a code that does not have a name in the Linux kernel. When specifying event codes by numeric value, you need to add a `%` character before the number to avoid ambiguity:
```
    key:%30   # Represents the event (EV_KEY, 30), equivalent to key:a or (EV_KEY, KEY_A)
    btn:%300  # Represents the event (EV_KEY, 300), has no name in the Linux kernel

    # While acknowledging that the % character looks silly, the following would be
    # ambiguous if this rule didn't exist:
    key:%1    # Represents the event (EV_KEY, 1), equivalent to key:esc or (EV_KEY, KEY_ESC)
    key:1     # Represents the event (EV_KEY, KEY_1), equivalent to key:%2 or (EV_KEY, 2)
```

**Domains**

Domains are not something that exists according to the evdev protocol, they are merely a tool invented by evsieve to help you write advanced maps. Domains are strings of text. Any event being processed by evsieve has a domain attached to it. This domain can be specified using the `domain=` clause on an `--input` argument, otherwise the domain of an event is set to the path to the input device that emitted said event.

You can specify a domain on a map source or target by adding @domain after the rest of the key. For example, a source of `key:a@my-domain` means "any event with type `EV_KEY`, code `KEY_A` and domain `my-domain`". If no domain is specified on a source, it will be interpreted as "any domain". If a domain is specified on a target, then the generated event will have that domain. If no domain is specified on a target, the generated event will have the same domain as the source event.

If the part before the @ is empty, then it will be interpreted as "any event with this domain", for example `--map @foo @bar` will turn any event with domain foo into the same event with domain bar. This is particularly handy for output devices: `--output @foo` will write all events with domain `foo` to a virtual device and leave the other events untouched.

**The yield flag**

It is possible to add the `yield` flag to an `--map` or `--copy` argument, for example:

```
    --map yield key:a key:b
```

The position of the `yield` flag does not matter. All events generated by a `--map yield` or `--copy yield` will not be processed by any further maps, copies, hooks, toggles, blocks, or any other kind of argument except for `--output`. For example, the following arguments will transpose the A and B keys:

```
    --map yield key:a key:b \
    --map yield key:b key:a \
```

If `yield` was not provided here, then the second map would turn any B-key events generated by the first map back into A-key events.

If a `yield` flag is provided to a `--copy` argument, then source events matching the `--copy` argument are *not* yielded, only the generated events are. For example, the following arguments will turn the A key into a B+C key.

```
    --copy yield key:a key:b \
    --map key:a key:c \
    --map key:b key:d \
```

**The `--block` argument**

The `--block` arguments have the form:

```
    --block [SOURCE_EVENT...]
```

The `--block` argument takes a list of event filters with the same format as the source events for `--map`s. All events that match one of the filters provided will be removed from the processing stream. For example, the following argument will drop all events related to the A or B keys:

```
    --block key:a key:b
```

Using `--block` is equivalent to using maps that generate zero output events; the above argument would have the same effect as `--map key:a --map key:b`.

If no source events are specified, the `--block` argument will drop all events from the processing stream.

**The `--merge` argument**

The `--merge` arguments have the form:

```
    --merge [SOURCE_EVENT...]
```

`--merge` is useful if you want to map multiple keys to a single one. For example, consider the following script:

```
evsieve --input /dev/input/by-id/keyboard \
        --map key key:enter \
        --output
```

This will map every key on the keyboard to the Enter key. If the user were to press two keys simultaneously, e.g. the A and B keys, then the output device would receive two KEY_DOWN events on the Enter key, followed up by two KEY_UP events on the Enter key. This by itself is not a big problem: if a key is already down, all subsequent KEY_DOWN events are ignored. However, it can have a counterintuitive result: as soon as one of the two (A, B) keys is released, the Enter key is released, even if the other key is still held down.

The following table shows an example how a serie of keystrokes on the keyboard would affect the visible state of the output keyboard if the above script were to be used:

Input event | State of input keyboard | Output event | State of output keyboard
------------|-------------------------|--------------|-------------------------
`key:a:1`   | The A key is down       | `key:enter:1`| The Enter key is down
`key:b:1`   | The A,B keys are down   | `key:enter:1`| The Enter key is down
`key:b:0`   | The A key is down       | `key:enter:0`| The Enter key is up
`key:c:1`   | The A,C keys are down   | `key:enter:1`| The Enter key is down
`key:c:0`   | The A key is down       | `key:enter:0`| The Enter key is up
`key:a:0`   | All keys are up         | `key:enter:0`| The Enter key is up

This is where the `--merge` argument jumps in. `--merge` keeps track of how many events of type KEY_DOWN and KEY_UP it has seen for all keys. It drops KEY_DOWN events for keys that are already down, and also drops KEY_UP events until the amount of KEY_UPs it has seen are equal to the amount of KEY_DOWNs it has seen. If we amend the previous script to include a `--merge` after the `--map`, the table turns into the following:

```
evsieve --input /dev/input/by-id/keyboard \
        --map key key:enter \
        --merge \
        --output
```

Input event | State of input keyboard | Output event | State of output keyboard
------------|-------------------------|--------------|-------------------------
`key:a:1`   | The A key is down       | `key:enter:1`| The Enter key is down
`key:b:1`   | The A,B keys are down   | (none)       | The Enter key is down
`key:b:0`   | The A key is down       | (none)       | The Enter key is down
`key:c:1`   | The A,C keys are down   | (none)       | The Enter key is down
`key:c:0`   | The A key is down       | (none)       | The Enter key is down
`key:a:0`   | All keys are up         | `key:enter:0`| The Enter key is up

It is possible to specify a filter after the `--merge` argument to make it apply to only a specific set of events, e.g. `--merge key:a` will only merge (EV_KEY, KEY_A) events and leave other events untouched. If no filter is specified, `--merge` will apply to all events of type EV_KEY.

Only events that have the same event code and domain are merged with each other.

**The `--delay` argument**

The `--delay` argument has the following basic syntax:

```
    --delay [EVENTS...] period=SECONDS
```

Example usages are:

```
    --delay period=1          # Delays all events by one second.
    --delay key:a period=0.2  # Delays all events related to the A key by 0.2 seconds.
```

The `--delay` argument removes all events that match one of the specified EVENTS from the event stream. If no EVENTS are specified, it removes all events from the event stream. All events that it removes will be added back to the event stream after an amount of seconds specified by the `period` flag passes. This effectively makes the events reach the further arguments at a later point in time.

## Toggles

The `--toggle` argument has the following basic syntax:

```
    --toggle SOURCE_EVENT TARGET_EVENT... [id=ID] [mode=consistent|passive]
```

Toggles work the same way as `--map`s do, with one difference: a `--map` will map each source event to all of its target events, whereas a `--toggle` will map each source events to only one of its target events. The target event it gets mapped to is called the "active target".

The first target listed becomes the active target when the program starts. The active target can be changed using a `--hook` with a `toggle` clause. For more information, see the "Toggles" subsection under the "Hooks" section.

An optional `id=` clause can be specified to give this `--toggle` a name. This ID can be used to modify specific toggles in the `--hook toggle=` clause. No two toggles may have the same ID.

**Modes**

There are two modes of operation for toggles: consistent and passive. The mode of operation can be chosen by supplying a `mode=` clause to a `--toggle` argument. If no mode is specified, then "consistent" will be chosen by default.

The easiest mode to understand is "passive". If `mode=passive` is specified on a toggle, then all source events will be mapped to the currently active target event, no exceptions. However, this mode of operation can introduce some unexpected issues. For example, consider the following argument:

```
    --toggle key:a key:b key:c mode=passive \
    --hook key:z toggle
```

Suppose the user presses the A key. Then a B key down event will be generated. Now suppose the user presses the Z key before releasing the A key. When the user releases the A key, the A key up event will be mapped to a C key up event, which results in the B key being stuck in a key_down state.

To address this problem, `mode=consistent` exists. If a toggle operates in consistent mode, then for every key it will remember which target was active when it received a key_down event of that key, and and will then map all events related to that key to that target until a key_up event of that key is received, even if the active target changed in the meanwhile. In the above example, this ensures that an A key up event is mapped to a B key up event event if the active target was changed.

## Hooks

The `--hook` argument has the following basic syntax:

```
    --hook KEY... [exec-shell=COMMAND]... [toggle[=[ID][:INDEX]]]... [send-key=KEY]... [sequential] [period=SECONDS] [breaks-on=...]
```

Hooks take actions when it receives a certain combination events. By default, they do not modify the event stream. An example of such an action is executing a certain script. The following hook will print "Hello, world!" every time LCtrl+A is pressed:

```
    --hook key:a key:leftcontrol exec-shell="echo Hello, world!"
```

One or more KEYs can be specified. The syntax for specifying the keys that trigger the hook is the same as the one used to match events for maps, but the semantics are different. The simple explanation of KEYs is that the hook will trigger whenever all those keys are held down simultaneously, and that is probably all you need to remember about them.

The clauses/flags `sequential`, `period`, and `breaks-on` can be provided to add additional restrictions to when the hook can trigger. The clauses `exec-shell`, `toggle`, or `send-key` can be used to decide what to do when the hook triggers.

**In detail: key format**

As said, the syntax is the same as the syntax used for maps, but the semantics are different. These semantics are intended to facilitate writing hooks that trigger when a certain combination of keys such as lctrl+A is pressed. First, if no event value is specified for a key, then the value is interpreted to be "1~". Thus, the above example is equivalent to:

```
    --hook key:a:1~ key:leftcontrol:1~ exec-shell="echo Hello, world!"
```

Formally said, the hook triggers when (1) an event is received that matches one of the KEYs specified, (2) the last event whose type, code and domain matched that same KEY did not have a value matching that KEY, and (3) for all other KEYs, the last event whose type, code and domain matched those KEYs had a value matching those KEYs.

It is not possible to specify transitions in these KEYs, e.g. `--hook key:a:1..0` will throw an error.

**Exec-shell**

If an `exec-shell` clause is specified, then a certain command will be executed using the system's default POSIX shell (`/bin/sh`). Thus, if `exec-shell="echo Hello, world!"` is specified, the following will be executed:

```
    /bin/sh -c "echo Hello, world!"
```

This will be executed as the same user that evsieve is running as, which may be root. If you want to run it as another user, you can make `sudo -u $USER` part of the command.

The command is executed asynchronously, which means that the processing of events will *not* halt until the command's execution has completed. In case the command takes sufficiently long to complete and the user presses keys sufficiently fast, it may be possible to trigger the hook again before the command's execution has finished, which will lead to multiple copies of the command being executed simultaneously.

Any processes spawned by `exec-shell` that are still running when evsieve exits will be sent a SIGTERM signal. This is may change in future versions of evsieve.

**Toggles**

Hooks are capable of modifying the active target of `--toggle` arguments specified elsewhere in the script. Any hook can modify any toggle, it doesn't matter whether the `--hook` or the `--toggle` argument was specified first, e.g. the following two orders are functionally identical:

```
    # Order 1
    --hook key:leftctrl toggle \
    --toggle @source @target-1 @target-2

    # Order 2, functionally identical to Order 1
    --toggle @source @target-1 @target-2 \
    --hook key:leftctrl toggle
```

If a `toggle` flag is specified on a hook, then all toggles specified anywhere in the script will have their active target moved to the next one, wrapping around. The above example maps all events with domain `source` to domain `target-1` by default. Once the lctrl key is pressed, they will start mapping the events to `target-2` instead. By pressing lctrl again, they will start mapping the events to `target-1` again.

It is also possible to specify a toggle clause, using the syntax:

```
    --hook toggle=[ID][:TARGET]
```

Both the ID and the TARGET are optional. If an ID is specified, then only the `--toggle` with the matching ID will be affected. For example, the following hook will affect the first toggle but not the second:

```
    --toggle @source-1 @target-1 @target-2 id=first-toggle \
    --toggle @source-2 @target-1 @target-2 id=second-toggle \
    --hook key:leftctrl toggle=first-toggle
```

By default, the active target is always moved to the next one in the list. It is also possible to move the active target to a specific one by providing a numerical index for TARGET, for example:

```
    --toggle @source-1 @target-1 @target-2 id=first-toggle \
    --toggle @source-2 @target-1 @target-2 id=second-toggle \
    --hook key:leftctrl toggle=first-toggle:1 \
    --hook key:rightctrl toggle=:2 \
```

This will move the active target to the first one (`@target-1`) for `first-toggle` when lctrl is pressed, and move the active target to the second one (`@target-2`) for all toggles when rctrl is pressed.

**Send-key**

The `send-key` clause allows you to send EV_KEY-type events when the hook triggers, for example:

```
    --hook key:leftctrl key:z send-key=key:f1
```

This will send a KEY_DOWN event for the F1 key when Ctrl+A is pressed, and sends a corresponding KEY_UP event when either the Ctrl key or the Z key is released.

**Sequential**

If the `sequential` flag is specified on a hook, then all KEYs associated with the hook additionally need to arrive in the specified order to trigger the hook. For example:

```
    --hook key:leftctrl key:z sequential exec-shell="echo Hello, world!"
```

This hook will only print `Hello world!` if you first press the LCtrl key and thereafter press the Z key. This hook will do nothing if you first press the Z key and thereafter the LCtrl key.

To avoid confusion, here is what the `sequential` flag does *not* do:

* The `sequential` flag does not forbid other events from arriving between the specified events. For example, pressing LCtrl+X+Z will still trigger the hook;
* The `sequential` flag does not remove the requirement that all specified keys must be pressed at the same time. For example, pressing LCtrl, releasing LCtrl, and then pressing Z, will not trigger the hook.

**Period**

The `period=SECONDS` clause adds the requirement that all KEYs associated with the hook need to be pressed within a certain amount of seconds, otherwise the hook won't trigger. For example:

```
    --hook key:leftctrl key:z period=0.5 exec-shell="echo Hello, world!"
```

This hook will only print `Hello, world!` if the LCtrl and Z keys are pressed within half a second of each other. If more than half a second passes after pressing either key before the other key is pressed, nothing happens.

**Breaks-on**

By default, the `--hook` agument does not care about events that do not match any of its KEYs. For example,

```
    --hook key:leftctrl key:z sequential exec-shell="echo Hello, world!"
```

will trigger on LCtrl+Z, but also on LCtrl+X+Z. As long as both LCtrl+Z are pressed at the same time, it does not matter how many other events arrive in the meanwhile.

The `breaks-on` clause allows you to prevent the hook from triggering if the hook receives a certain event before the key combination is completed. For examle,

```
    --hook key:leftctrl key:z breaks-on=key:x:1 exec-shell="echo Hello, world!"
```

will print `Hello, world!` if you press LCtrl+Z, but not if you press LCtrl+X+Z, because the `key:x:1` event would match the breaks-on clause. The combination `LCtrl+Y+Z` would still cause the hook to trigger.

An event that contributes towards activating the hook will never break it. This makes it possible to use something like the following to break the combination when any key other than LCtrl or Z is pressed:

```
    --hook key:leftctrl key:z breaks-on=key::1 exec-shell="echo Hello, world!"
```

**The `--withhold` argument**

The `--withhold` argument must directly follow one or multiple consective `--hook` arguments and has the following basic syntax:

```
    --withhold [KEY...]
```

The `--withhold` argument is useful when you want to trigger a hook on a certain key combination, but do not want those keys to reach the output device if they trigger the hook. This is conceptually non-trivial when that hook requires more than one key to trigger, because when the first event of a combination arrives it is unclear whether the other required events will follow, and it is impossible to undo writing an event to an output device.

Any event that may trigger one of the consecutively preceding hooks will be removed from the event stream and withheld until either (1) it becomes clear that the event will not actually trigger any of the preceding hooks, in which case it is put back in the event stream, or (2) it actually triggers one of the preceding hooks, in which case the event is definitively dropped.

For example, let's say you want to intercept the combinations Ctrl+A and Ctrl+B. Consider the following script:

```
evsieve --input /dev/input/by-id/my-keyboard \
        --hook key:leftctrl key:a exec-shell="echo Pressed Ctrl+A" \
        --hook key:leftctrl key:b exec-shell="echo Pressed Ctrl+B" \
        --withhold \
        --output
```

This script takes an absolutionist approach: a `key:leftctrl:1` event will not be sent to the output device unless it is clear that this event will not contribute to activating the preceding hooks, which only becomes clear when the Ctrl key gets released, in which case the output device will receive the `key:leftctrl:1`, `key:leftctrl:0` events directly after each other.

This leads to the unintended side effect of making it impossible to type key combinations like Ctrl+X, because the Ctrl key gets withheld until it is released. To get closer to the intended goal of only blocking the Ctrl+A and Ctrl+B keys, two things can be done:

* You can make the `--withhold` argument apply to only some keys;
* You can add additional restrictions to the preceding hooks.

The following script comes much closer to the intended effect:

```
evsieve --input /dev/input/by-id/my-keyboard \
        --hook key:leftctrl key:a exec-shell="echo Pressed Ctrl+A" sequential \
        --hook key:leftctrl key:b exec-shell="echo Pressed Ctrl+B" sequential \
        --withhold key:a key:b \
        --output
```

By restricting the `--withhold` argument to only the A and B keys, we ensured that the Ctrl key never gets withheld, so that we can enter other combinations like Ctrl+X without issue.

Adding the `sequential` flag to the hooks ensures that the A and B keys will not be withheld unless the Ctrl key is already pressed before them: the combination A+Ctrl cannot trigger a sequential hook, so there is no point in withholding the A key if Ctrl is not pressed already.

Although it is not necessary in this case, the `--hook period=...` clause is also often useful in combination with the `--withhold` argument to ensure that events are not withheld for more than a certain amount of time.

Important to note is that the `--withhold` argument applies to all consecutive preceding hooks. So in the above example, the `--withhold` argument will withold potential combinations for both the preceding hooks. However, in the following script, the `--withhold` argument will not withhold the C or D keys, because there is a non-hook argument between them and the `--withhold` argument:

```
evsieve --input /dev/input/by-id/my-keyboard \
        --hook key:leftctrl key:c exec-shell="echo Pressed Ctrl+C" sequential \
        --hook key:leftctrl key:d exec-shell="echo Pressed Ctrl+D" sequential \
        --map  key:x key:y \
        --hook key:leftctrl key:a exec-shell="echo Pressed Ctrl+A" sequential \
        --hook key:leftctrl key:b exec-shell="echo Pressed Ctrl+B" sequential \
        --withhold \
        --output
```

## Inputs

The `--input` argument has the following basic syntax:

```
    --input PATH [PATH...] [domain=DOMAIN] [grab[=auto|force]] [persist=reopen|none|exit]
```

At least one path to a device to open is mandatory, everything else is optional. All paths must be represented in absolute form, i.e. starting with a "/" character. It is possible to provide more than one path, in which case multiple devices will be opened with a single argument.

If the `domain=` clause is provided, then all events read from this input device will have the specified domain attached to them, otherwise the domain of those events shall be equal to the path of said input device. Domains have no intrinsic meaning, but are useful for writing maps. See the "Key format" section under "In detail: Maps" for more information.

**Grab modes**

Using the `grab` clause, it is possible to "grab" an input device, by which evsieve will claim exclusive reading access to said device and prevent other programs from reading from that device. This will prevent the X server and similar programs from acting upon the events generated by said device.

Evsieve has two different modes of grabbing a device, `auto` and `force`. If merely the `grab` flag is specified, it will be interpreted as `grab=auto`. If no `grab` flag or clause is specified, the input device will not be grabbed.

The two modes differ only in when the input device is grabbed. Under the `force` mode, evsieve will immediately grab the event device as soon as evsieve starts. Under the `auto` mode, evsieve will not grab the device until no keys are in the "down" state.

Depending on your use case, there may be an annoying issue with the `force` mode: if you start evsieve from a terminal by typing in a command or scriptname and then press enter, evsieve will probably grab your device before you release the enter key, causing the terminal/X server to not see you release the enter key and treat it as stuck in the key_down state. The `auto` mode solves this by not actually grabbing your device until the enter key (and all other keys) are released.

The `auto` mode is considered the default mode because it has the least potential for surprises. However, if evsieve does refuses to grab your device because some key is permanently stuck in the down state for some reason, you may want to specify `grab=force`.

There are some questions left surrounding the design of the `auto` mode, so it is possible that its behaviour will change in future versions of evsieve.

**Persistence**

The `persist=` clause tells evsieve what to do in case it somehow fails to read events from input devices, most likely because the USB cable has been disconnected.

For the sake of backwards compatibility, the default mode is `persist=none`, which tells evsieve to close any device that it fails to read events from and try to continue without said device. If all input devices have been closed, evsieve will exit.

If `persist=exit` has been specified, evsieve exit whenever this input device disconnects, even if other input devices are still available.

If `persist=reopen` has been specified, evsieve will instead wait until the input device becomes available again and then try to reopen it. Even if `persist=reopen` is used, all input devices must be available when `evsieve` starts.

If the closed and reopened input devices are somehow not identical, evsieve may destroy and recreate some virtual output devices if necessary to ensure all virtual output devices have the correct capabilities.

## Outputs

The basic syntax for the `--output` argument is:

```
    --output [EVENTS...] [create-link=PATH] [name=NAME] [repeat[=enable|disable|passive]]
```

The `--output` argument creates a virtual event device and sends events to it. If the `--output` argument is specified multiple times, a different virtual device will be created for each argument.

By default, all events that reach the `--output` argument will be sent to its virtual device, but it is possible to specify a filter to send only some of the events events that reach it. Any event that get sends to an output device is removed from the evsieve processing stream, e.g. in the following example:

```
evsieve --input /dev/input/by-id/keyboard \
        --output \
        --output
```

All events generated by the `--input` argument will be written to a virtual device created by the first `--output` argument. No events will reach the second output argument because all events have already been consumed by the first.

It is possible to create a filter upon which events to send to an output device by specifying the `EVENTS` part. The syntax used for matching these `EVENTS` is the same as the one used by the `--map` argument, see the "Maps" section above. If zero `EVENTS` are specified, all events will be written to the output device. If one or more `EVENTS` are specified, only events matching at least one of the `EVENTS` are written to the output device. For example:

```
evsieve --input /dev/input/by-id/keyboard \
        --output key:a key:b \
        --output
```

This script will write all events relating to the A or B keys to the first output device, and all other events to the second output device.

**Symlinks**

It is possible to ask evsieve to create a symlink to the device created by an `--output` by specifying the `--create-link=` part. For example, `--output create-link=/dev/input/by-id/my-virtual-device` will create a symlink at `/dev/input/by-id/my-virtual-device` that points to the actual event device node. The actual event device node will probably have an unpredictable name of `/dev/input/event__` where `__` is an arbitrary number.

It is customary for links to event devices to reside in `/dev/input/by-id/`, but this is by no means a requirement. You can create a link anywhere you want (as long as evsieve has write permission to the directory where you put it). Note that putting links in `/dev/input/by-id/` requires root privileges on most distributions, so if you want to run evsieve with less privileges, you may want to put your links elsewhere.

Evsieve will create the link when it starts, and try to remove the link when it exits. Note that there are circumstances under which evsieve may be unable to clean up the link it created, such as when evsieve is SIGKILL'd or in case of unexpected power loss. In such cases you may end up with a dangling symlink on your filesystem.

In case a symlink already exists at the path you provided to `create-link=`, evsieve will overwrite that link. This behaviour has been chosen to not make any scripts involving evsieve mysteriously break after an unexpected power loss.

**Names**

A name for the device can be specified using the `name=` clause, e.g.:

```
    --output name="My keyboard"
```

If no name is specified, then `Evsieve Virtual Device` is chosen by default. The device name is usually of little consequence, but some third-party tools may care about it. For example, the `evtest` utility is able to display the device name.

**Repeats**

Some devices, like most keyboards, will send repeat events when a key or button is held down. These are events with type EV_KEY and value 2. Most applications ignore these repeat events and use their own internal logic to detect keys that are held down, but for correctness' sake, evsieve is capable of handling them.

Repeat events are read from input devices if input devices emit them and processed just like any other event. There are three things an output device can do when it receives repeat events:

* If `repeat=passive` is set on an `--output` device, then all repeat events that reach this `--output` argument will be written to this device;
* If `repeat=disable` is set on an `--output` device, then all repeat events that reach this `--output` argument will be dropped;
* If `repeat=enable` is set on an `--output` device, then all repeat events that reach this `--output` argument *will be dropped*, but the kernel will be asked to automatically generate repeat events for this device.

If no `repeat=` clause is specified, then `repeat=passive` will be chosen by default. If a `repeat` flag is specified without a mode, then `repeat=enable` is chosen.

## Prints

The basic syntax for the `--print` argument is:

```
    --print [EVENTS...] [format=default|direct]
```

The `--print` arguments prints all events in the event processing stream to stdout. It does not modify the event processing stream. This is mostly useful for debugging your scripts and for discovering what evsieve calls certain events.

It prints the events the way they are seen by evsieve at the point where the `--print` argument is placed. For example, the following script will print `key:b` events when the A key is pressed:

```
evsieve --input /dev/input/by-id/keyboard \
        --map key:a key:b \
        --print
```

The optional EVENTS can be a filter for events that you want to print, similar to the EVENTS in the `--output` argument. For example, `--print @foo` will only print events that have domain `foo`. If no filter is specified, all events are printed.

**Formats**

The `format=` clause can specify how the events should be printed, and can choose between `format=default` and `format=direct`. If no `format=` clause is specified, `format=default` shall be assumed.

The default formatter prints events in a human-readable way, for example:

```
Event:  type:code = key:a          value = 1 (down)    domain = /dev/input/by-id/keyboard
```

If you specify `format=direct`, then it will print the events using evsieve's standard key format that's also used for maps and other arguments, for example:

```
key:a:1@/dev/input/by-id/keyboard
```

Note: `--print` is intended for human readers, not for scripts. Even if `format=<something>` is specified, evsieve makes absolutely no guarantees about how the events are printed. Future versions of evsieve may change the format of the printed events without warning. It is not recommended to attempt to programmatically parse the output of evsieve.

# License
Most of the content on this repository is licensed under the GPLv2 or later, though the repository does contain some files derived from third-party content with a different license, including some content indirectly derived from Linux kernel headers which, depending on legal interpretation, may or may not effectively render the program GPLv2 only. To the extent of our knowledge, all content on this repository is compatible with the GPLv2. See the COPYING file for more information.

This program is distributed in the hope that it will be useful, **but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.** See the GNU General Public License for more details.
07070100000006000081A4000000000000000000000001647DB230000000CF000000000000000000000000000000000000001900000000evsieve-1.4.0~0/build.rs// SPDX-License-Identifier: GPL-2.0-or-later

fn main() {
    println!("cargo:rustc-link-lib=dylib=evdev");

    if cfg!(feature = "systemd") {
        println!("cargo:rustc-link-lib=dylib=systemd");
    }
}07070100000007000081ED000000000000000000000001647DB230000001D8000000000000000000000000000000000000002500000000evsieve-1.4.0~0/generate_bindings.sh#!/bin/bash

cd "$(dirname "${BASH_SOURCE[0]}")"
cat licenses/thirdparty/libevdev/header.txt | sed 's/^/\/\/ /' > src/bindings/libevdev.rs 
bindgen --whitelist-type 'libevdev_.*' \
        --whitelist-function 'libevdev_.*' \
        --whitelist-var 'EV_.*' \
        --whitelist-var 'REP_.*' \
        --whitelist-var 'MSC_.*' \
        --no-doc-comments \
        src/bindings/libevdev.h \
        -- -I"/usr/include/libevdev-1.0/" \
        >> src/bindings/libevdev.rs
07070100000008000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001900000000evsieve-1.4.0~0/licenses07070100000009000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000002400000000evsieve-1.4.0~0/licenses/thirdparty0707010000000A000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000002B00000000evsieve-1.4.0~0/licenses/thirdparty/crates0707010000000B000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000003700000000evsieve-1.4.0~0/licenses/thirdparty/crates/lazy_static0707010000000C000081A4000000000000000000000001647DB23000002A5E000000000000000000000000000000000000004200000000evsieve-1.4.0~0/licenses/thirdparty/crates/lazy_static/apache.txt                              Apache License
                        Version 2.0, January 2004
                     http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
       Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
       stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
       that You distribute, all copyright, patent, trademark, and
       attribution notices from the Source form of the Work,
       excluding those notices that do not pertain to any part of
       the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
       distribution, then any Derivative Works that You distribute must
       include a readable copy of the attribution notices contained
       within such NOTICE file, excluding those notices that do not
       pertain to any part of the Derivative Works, in at least one
       of the following places: within a NOTICE text file distributed
       as part of the Derivative Works; within the Source form or
       documentation, if provided along with the Derivative Works; or,
       within a display generated by the Derivative Works, if and
       wherever such third-party notices normally appear. The contents
       of the NOTICE file are for informational purposes only and
       do not modify the License. You may add Your own attribution
       notices within Derivative Works that You distribute, alongside
       or as an addendum to the NOTICE text from the Work, provided
       that such additional attribution notices cannot be construed
       as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

   To apply the Apache License to your work, attach the following
   boilerplate notice, with the fields enclosed by brackets "[]"
   replaced with your own identifying information. (Don't include
   the brackets!)  The text should be enclosed in the appropriate
   comment syntax for the file format. We also recommend that a
   file or class name and description of purpose be included on the
   same "printed page" as the copyright notice for easier
   identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.0707010000000D000081A4000000000000000000000001647DB2300000042E000000000000000000000000000000000000003F00000000evsieve-1.4.0~0/licenses/thirdparty/crates/lazy_static/mit.txtCopyright (c) 2010 The Rust Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.0707010000000E000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000003000000000evsieve-1.4.0~0/licenses/thirdparty/crates/libc0707010000000F000081A4000000000000000000000001647DB23000002A5E000000000000000000000000000000000000003B00000000evsieve-1.4.0~0/licenses/thirdparty/crates/libc/apache.txt                              Apache License
                        Version 2.0, January 2004
                     http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
       Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
       stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
       that You distribute, all copyright, patent, trademark, and
       attribution notices from the Source form of the Work,
       excluding those notices that do not pertain to any part of
       the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
       distribution, then any Derivative Works that You distribute must
       include a readable copy of the attribution notices contained
       within such NOTICE file, excluding those notices that do not
       pertain to any part of the Derivative Works, in at least one
       of the following places: within a NOTICE text file distributed
       as part of the Derivative Works; within the Source form or
       documentation, if provided along with the Derivative Works; or,
       within a display generated by the Derivative Works, if and
       wherever such third-party notices normally appear. The contents
       of the NOTICE file are for informational purposes only and
       do not modify the License. You may add Your own attribution
       notices within Derivative Works that You distribute, alongside
       or as an addendum to the NOTICE text from the Work, provided
       that such additional attribution notices cannot be construed
       as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

   To apply the Apache License to your work, attach the following
   boilerplate notice, with the fields enclosed by brackets "[]"
   replaced with your own identifying information. (Don't include
   the brackets!)  The text should be enclosed in the appropriate
   comment syntax for the file format. We also recommend that a
   file or class name and description of purpose be included on the
   same "printed page" as the copyright notice for easier
   identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.07070100000010000081A4000000000000000000000001647DB23000000433000000000000000000000000000000000000003800000000evsieve-1.4.0~0/licenses/thirdparty/crates/libc/mit.txtCopyright (c) 2014-2020 The Rust Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.07070100000011000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000002D00000000evsieve-1.4.0~0/licenses/thirdparty/libevdev07070100000012000081A4000000000000000000000001647DB2300000045C000000000000000000000000000000000000003800000000evsieve-1.4.0~0/licenses/thirdparty/libevdev/header.txtSPDX-License-Identifier: MIT

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.07070100000013000081A4000000000000000000000001647DB230000005DB000000000000000000000000000000000000003900000000evsieve-1.4.0~0/licenses/thirdparty/libevdev/license.txtSPDX-License-Identifier: MIT

Copyright © 2013 Red Hat, Inc.
Copyright © 2013 David Herrmann <dh.herrmann@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.

The following license is from a Linux kernel header file and there is no GPL
code this package links to.

Copyright (c) 1999-2002 Vojtech Pavlik

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
07070100000014000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000002F00000000evsieve-1.4.0~0/licenses/thirdparty/libsystemd07070100000015000081A4000000000000000000000001647DB230000067A1000000000000000000000000000000000000003800000000evsieve-1.4.0~0/licenses/thirdparty/libsystemd/lgpl.txt                  GNU LESSER GENERAL PUBLIC LICENSE
                       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

                  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

                            NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

                     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!07070100000016000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000002A00000000evsieve-1.4.0~0/licenses/thirdparty/linux07070100000017000081A4000000000000000000000001647DB230000046AC000000000000000000000000000000000000003500000000evsieve-1.4.0~0/licenses/thirdparty/linux/gpl_v2.txt                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
07070100000018000081A4000000000000000000000001647DB23000000150000000000000000000000000000000000000004E00000000evsieve-1.4.0~0/licenses/thirdparty/linux/linux_kernel_input_codes_notice.txtSPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note

Copyright (c) 1999-2002 Vojtech Pavlik
Copyright (c) 2015 Hans de Goede <hdegoede@redhat.com>

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
07070100000019000081A4000000000000000000000001647DB230000004E8000000000000000000000000000000000000004100000000evsieve-1.4.0~0/licenses/thirdparty/linux/linux_syscall_note.txtSPDX-Exception-Identifier: Linux-syscall-note
SPDX-URL: https://spdx.org/licenses/Linux-syscall-note.html
SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+, GPL-2.0-only, GPL-2.0-or-later
Usage-Guide:
  This exception is used together with one of the above SPDX-Licenses
  to mark user space API (uapi) header files so they can be included
  into non GPL compliant user space application code.
  To use this exception add it with the keyword WITH to one of the
  identifiers in the SPDX-Licenses tag:
    SPDX-License-Identifier: <SPDX-License> WITH Linux-syscall-note
License-Text:

   NOTE! This copyright does *not* cover user programs that use kernel
 services by normal system calls - this is merely considered normal use
 of the kernel, and does *not* fall under the heading of "derived work".
 Also note that the GPL below is copyrighted by the Free Software
 Foundation, but the instance of code that it refers to (the Linux
 kernel) is copyrighted by me and others who actually wrote it.

 Also note that the only valid version of the GPL as far as the kernel
 is concerned is _this_ particular version of the license (ie v2, not
 v2.2 or v3.x or whatever), unless explicitly otherwise stated.

			Linus Torvalds0707010000001A000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000002900000000evsieve-1.4.0~0/licenses/thirdparty/rust0707010000001B000081A4000000000000000000000001647DB23000002469000000000000000000000000000000000000003300000000evsieve-1.4.0~0/licenses/thirdparty/rust/COPYRIGHTShort version for non-lawyers:

The Rust Project is dual-licensed under Apache 2.0 and MIT
terms.


Longer version:

Copyrights in the Rust project are retained by their contributors. No
copyright assignment is required to contribute to the Rust project.

Some files include explicit copyright notices and/or license notices.
For full authorship information, see the version control history or
https://thanks.rust-lang.org

Except as otherwise noted (below and/or in individual files), Rust is
licensed under the Apache License, Version 2.0 <LICENSE-APACHE> or
<http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
<LICENSE-MIT> or <http://opensource.org/licenses/MIT>, at your option.


The Rust Project includes packages written by third parties.
The following third party packages are included, and carry
their own copyright notices and license terms:

* LLVM. Code for this package is found in src/llvm-project.

    Copyright (c) 2003-2013 University of Illinois at
    Urbana-Champaign.  All rights reserved.

    Developed by:

        LLVM Team

        University of Illinois at Urbana-Champaign

        http://llvm.org

    Permission is hereby granted, free of charge, to any
    person obtaining a copy of this software and associated
    documentation files (the "Software"), to deal with 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:

        * Redistributions of source code must retain the
          above copyright notice, this list of conditions
          and the following disclaimers.

        * Redistributions in binary form must reproduce the
          above copyright notice, this list of conditions
          and the following disclaimers in the documentation
          and/or other materials provided with the
          distribution.

        * Neither the names of the LLVM Team, University of
          Illinois at Urbana-Champaign, nor the names of its
          contributors may be used to endorse or promote
          products derived from this Software without
          specific prior written permission.

    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 CONTRIBUTORS 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 WITH THE SOFTWARE.

* Additional libraries included in LLVM carry separate
  BSD-compatible licenses. See src/llvm-project/llvm/LICENSE.TXT
  for details.

* compiler-rt, in src/compiler-rt is dual licensed under
  LLVM's license and MIT:

    Copyright (c) 2009-2014 by the contributors listed in
    CREDITS.TXT

    All rights reserved.

    Developed by:

        LLVM Team

        University of Illinois at Urbana-Champaign

        http://llvm.org

    Permission is hereby granted, free of charge, to any
    person obtaining a copy of this software and associated
    documentation files (the "Software"), to deal with 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:

        * Redistributions of source code must retain the
          above copyright notice, this list of conditions
          and the following disclaimers.

        * Redistributions in binary form must reproduce the
          above copyright notice, this list of conditions
          and the following disclaimers in the documentation
          and/or other materials provided with the
          distribution.

        * Neither the names of the LLVM Team, University of
          Illinois at Urbana-Champaign, nor the names of its
          contributors may be used to endorse or promote
          products derived from this Software without
          specific prior written permission.

    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 CONTRIBUTORS 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 WITH THE SOFTWARE.

    ========================================================

    Copyright (c) 2009-2014 by the contributors listed in
    CREDITS.TXT

    Permission is hereby granted, free of charge, to any
    person obtaining a copy of this software and associated
    documentation files (the "Software"), to deal in the
    Software without restriction, including without
    limitation the rights to use, copy, modify, merge,
    publish, distribute, sublicense, and/or sell copies of
    the Software, and to permit persons to whom the Software
    is furnished to do so, subject to the following
    conditions:

    The above copyright notice and this permission notice
    shall be included in all copies or substantial portions
    of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.

* Portions of the FFI code for interacting with the native ABI
  is derived from the Clay programming language, which carries
  the following license.

    Copyright (C) 2008-2010 Tachyon Technologies.
    All rights reserved.

    Redistribution and use in source and binary forms, with
    or without modification, are permitted provided that the
    following conditions are met:

    1. Redistributions of source code must retain the above
       copyright notice, this list of conditions and the
       following disclaimer.

    2. Redistributions in binary form must reproduce the
       above copyright notice, this list of conditions and
       the following disclaimer in the documentation and/or
       other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
    USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
    OF SUCH DAMAGE.

* libbacktrace, under src/libbacktrace:

    Copyright (C) 2012-2014 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.

    Redistribution and use in source and binary forms, with
    or without modification, are permitted provided that the
    following conditions are met:

        (1) Redistributions of source code must retain the
        above copyright notice, this list of conditions and
        the following disclaimer.

        (2) Redistributions in binary form must reproduce
        the above copyright notice, this list of conditions
        and the following disclaimer in the documentation
        and/or other materials provided with the
        distribution.

        (3) The name of the author may not be used to
        endorse or promote products derived from this
        software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
    AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
    NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
    USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
    OF SUCH DAMAGE.  */0707010000001C000081A4000000000000000000000001647DB230000025FA000000000000000000000000000000000000003400000000evsieve-1.4.0~0/licenses/thirdparty/rust/apache.txt                              Apache License
                        Version 2.0, January 2004
                     http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
       Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
       stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
       that You distribute, all copyright, patent, trademark, and
       attribution notices from the Source form of the Work,
       excluding those notices that do not pertain to any part of
       the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
       distribution, then any Derivative Works that You distribute must
       include a readable copy of the attribution notices contained
       within such NOTICE file, excluding those notices that do not
       pertain to any part of the Derivative Works, in at least one
       of the following places: within a NOTICE text file distributed
       as part of the Derivative Works; within the Source form or
       documentation, if provided along with the Derivative Works; or,
       within a display generated by the Derivative Works, if and
       wherever such third-party notices normally appear. The contents
       of the NOTICE file are for informational purposes only and
       do not modify the License. You may add Your own attribution
       notices within Derivative Works that You distribute, alongside
       or as an addendum to the NOTICE text from the Work, provided
       that such additional attribution notices cannot be construed
       as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS0707010000001D000081A4000000000000000000000001647DB230000003FE000000000000000000000000000000000000003100000000evsieve-1.4.0~0/licenses/thirdparty/rust/mit.txtPermission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.0707010000001E000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001400000000evsieve-1.4.0~0/src0707010000001F000081A4000000000000000000000001647DB23000002581000000000000000000000000000000000000001E00000000evsieve-1.4.0~0/src/affine.rs// SPDX-License-Identifier: GPL-2.0-or-later

//! This module is intended for handling affine maps, such as
//!     --map abs:z abs:z:30-4x+d

use crate::error::ArgumentError;
use crate::event::Event;
use crate::capability::Capability;
use crate::range::{Range, ExtendedInteger};

#[derive(Clone, Copy, Debug)]
pub struct AffineFactor {
    absolute: f64,
    relative: f64,
    addition: f64,
}

impl AffineFactor {
    pub fn merge(&self, mut event: Event) -> Event {
        let absolute_factor = self.absolute * f64::from(event.value);
        // The following rounding is specially designed to avoid accumulating rounding
        // errors in cases like `--map abs:x rel:x:d`.
        let relative_factor =
            (f64::from(event.value) * self.relative).floor()
            - (f64::from(event.previous_value) * self.relative).floor();
        
        event.value = (
            (absolute_factor + self.addition).trunc() + relative_factor
        ) as i32;

        event
    }

    pub fn merge_cap(&self, mut cap: Capability) -> Capability {
        let min: f64 = cap.value_range.min.into();
        let max: f64 = cap.value_range.max.into();

        let trunc_boundaries = (
            (mul_zero(min, self.absolute) + self.addition).trunc(),
            (mul_zero(max, self.absolute) + self.addition).trunc(),
        );

        let relative_span = mul_zero(self.relative, max-min);

        // In case the relative factor is nonzero and the range is unbounded
        // on one end, then the following list will contain NaNs. In that case,
        // the range of events is everything.
        let possible_boundaries: [f64; 4] = [
            trunc_boundaries.0 - relative_span, trunc_boundaries.0 + relative_span,
            trunc_boundaries.1 - relative_span, trunc_boundaries.1 + relative_span,
        ];

        let new_range = if IntoIterator::into_iter(possible_boundaries).any(f64::is_nan) {
            Range::new(None, None)
        } else {
            let lower_end = IntoIterator::into_iter(possible_boundaries).reduce(f64::min);
            let upper_end = IntoIterator::into_iter(possible_boundaries).reduce(f64::max);
    
            Range::spanned_between(
                to_extended_or(lower_end, ExtendedInteger::NegativeInfinity),
                to_extended_or(upper_end, ExtendedInteger::PositiveInfinity),
            )
        };
        
        cap.value_range = new_range;
        cap
    }

    /// Returns Some(value) if this factor can be seen as a simple constant.
    pub fn as_constant(&self) -> Option<f64> {
        if self.absolute == 0.0 && self.relative == 0.0 {
            Some(self.addition)
        } else {
            None
        }
    }
}

/// A multiplication functions where 0*anything=0.
/// This helps avoiding 0*Infinity resulting in NaN.
fn mul_zero(x: f64, y: f64) -> f64 {
    if x == 0.0 || y == 0.0 {
        0.0
    } else {
        x * y
    }
}

/// Helper function for AffineFactor::merge_cap().
fn to_extended_or(source: Option<f64>, default: ExtendedInteger) -> ExtendedInteger {
    let source = match source {
        Some(value) => value,
        None => return default,
    };

    if source.is_nan() {
        return default;
    }
    if source == f64::INFINITY {
        return ExtendedInteger::PositiveInfinity;
    }
    if source == f64::NEG_INFINITY {
        return ExtendedInteger::NegativeInfinity;
    }

    ExtendedInteger::Discrete(source as i32)
}

struct Component {
    factor: f64,
    variable: Variable,
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum Variable {
    Value,
    Delta,
    One,
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum Sign {
    Positive,
    Negative,
}

enum Part {
    Sign(Sign),
    Numeric(Vec<char>),
    Variable(Variable),
}

fn lex_to_parts(source: &str) -> Result<Vec<Part>, ArgumentError> {
    let mut parts = Vec::new();
    if source.is_empty() {
        return Ok(parts);
    }

    for character in source.chars() {
        match character {
            '-' => parts.push(Part::Sign(Sign::Negative)),
            '+' => parts.push(Part::Sign(Sign::Positive)),
            '0' ..= '9' | '.' => {
                if let Some(Part::Numeric(vector)) = parts.last_mut() {
                    vector.push(character);
                } else {
                    parts.push(Part::Numeric(vec![character]));
                }
            },
            'x' => parts.push(Part::Variable(Variable::Value)),
            'd' => parts.push(Part::Variable(Variable::Delta)),
            _ => return Err(ArgumentError::new(format!("Invalid character: {}", character)))
        }
    }
    
    Ok(parts)
}

fn lex_to_components(source: &str) -> Result<Vec<Component>, ArgumentError> {
    let mut parts = lex_to_parts(source)?;
    
    // Add implicit first sign.
    match parts.first() {
        Some(Part::Sign(_)) => (),
        Some(_) => parts.insert(0, Part::Sign(Sign::Positive)),
        None => return Err(ArgumentError::new("Empty value.")),
    }

    let mut components: Vec<Component> = Vec::new();
    let mut parts_iter = parts.into_iter().peekable();
    loop {
        let sign = match parts_iter.next() {
            Some(Part::Sign(sign)) => sign,
            None => break,
            _ => return Err(ArgumentError::new("Expected sign, found something else.")),
        };
        let (numeric, variable) = match parts_iter.next() {
            Some(Part::Variable(variable)) => (vec!['1'], variable),
            Some(Part::Numeric(numeric)) => (numeric, match parts_iter.peek() {
                Some(&Part::Variable(variable)) => {
                    parts_iter.next();
                    variable
                },
                _ => Variable::One,
            }),
            _ => return Err(ArgumentError::new("Invalid expression.")),
        };

        let numeric_str = numeric.into_iter().collect::<String>();
        let number = match variable {
            Variable::One => numeric_str.parse::<i32>()
                .map_err(|_| ArgumentError::new("Cannot parse factor as integer."))?
                as f64,
            _ => numeric_str.parse::<f64>()
                .map_err(|_| ArgumentError::new("Cannot parse factor as number."))?,
        };

        let factor = match sign {
            Sign::Positive => number,
            Sign::Negative => -number,
        };
        
        components.push(Component { factor, variable });
    }

    Ok(components)
}

pub fn parse_affine_factor(source: &str) -> Result<AffineFactor, ArgumentError> {
    let components = lex_to_components(source)?;
    let mut result = AffineFactor {
        absolute: 0.0,
        relative: 0.0,
        addition: 0.0,
    };

    for component in components {
        match component.variable {
            Variable::Value => result.absolute += component.factor,
            Variable::Delta => result.relative += component.factor,
            Variable::One   => result.addition += component.factor,
        }
    }

    Ok(result)
}

#[test]
fn unittest() {
    let domain = crate::domain::get_unique_domain();
    let get_test_event = |value, previous_value| crate::event::Event {
        value, previous_value, domain,
        code: crate::event::EventCode::new(crate::event::EventType::new(1), 1),
        namespace: crate::event::Namespace::User,
    };
    let get_test_cap = |value_range| crate::capability::Capability {
        domain, value_range,
        code: crate::event::EventCode::new(crate::event::EventType::new(1), 1),
        namespace: crate::event::Namespace::User,
        abs_meta: None,
    };

    assert_eq!(
        parse_affine_factor("1").unwrap().merge(get_test_event(7, 13)),
        get_test_event(1, 13),
    );
    assert_eq!(
        parse_affine_factor("2x+1").unwrap().merge(get_test_event(7, 13)),
        get_test_event(15, 13),
    );
    assert_eq!(
        parse_affine_factor("-2.5x+5").unwrap().merge(get_test_event(8, 13)),
        get_test_event(-15, 13),
    );
    assert_eq!(
        parse_affine_factor("d+x").unwrap().merge(get_test_event(7, 13)),
        get_test_event(1, 13),
    );
    assert_eq!(
        parse_affine_factor("-d+x").unwrap().merge(get_test_event(7, 13)),
        get_test_event(13, 13),
    );
    assert_eq!(
        parse_affine_factor("5+0x").unwrap().merge(get_test_event(7, 13)),
        get_test_event(5, 13),
    );

    assert_eq!(
        parse_affine_factor("-d+x+1").unwrap().merge_cap(get_test_cap(Range::new(-2, 5))),
        get_test_cap(Range::new(-8, 13)),
    );
    assert_eq!(
        parse_affine_factor("-d+x+1").unwrap().merge_cap(get_test_cap(Range::new(None, 5))),
        get_test_cap(Range::new(None, None)),
    );
    assert_eq!(
        parse_affine_factor("-d+x+1").unwrap().merge_cap(get_test_cap(Range::new(-2, None))),
        get_test_cap(Range::new(None, None)),
    );
    assert_eq!(
        parse_affine_factor("-x").unwrap().merge_cap(get_test_cap(Range::new(-2, 5))),
        get_test_cap(Range::new(-5, 2)),
    );
    assert_eq!(
        parse_affine_factor("-x").unwrap().merge_cap(get_test_cap(Range::new(None, 7))),
        get_test_cap(Range::new(-7, None)),
    );
    assert_eq!(
        parse_affine_factor("8").unwrap().merge_cap(get_test_cap(Range::new(-2, 5))),
        get_test_cap(Range::new(8, 8)),
    );
    assert_eq!(
        parse_affine_factor("8").unwrap().merge_cap(get_test_cap(Range::new(None, None))),
        get_test_cap(Range::new(8, 8)),
    );
    

    assert!(parse_affine_factor("z").is_err());
    assert!(parse_affine_factor("--x").is_err());
    assert!(parse_affine_factor("x3").is_err());
}07070100000020000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001E00000000evsieve-1.4.0~0/src/arguments07070100000021000081A4000000000000000000000001647DB23000000200000000000000000000000000000000000000002800000000evsieve-1.4.0~0/src/arguments/config.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::{ArgumentError};
use crate::arguments::lib::ComplexArgGroup;

/// Represents a --map or --copy argument.
pub(super) struct ConfigArg {
    pub paths: Vec<String>,
}

impl ConfigArg {
	pub fn parse(args: Vec<String>) -> Result<ConfigArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &[],
            &[],
            true,
            false,
        )?;

        Ok(ConfigArg { paths: arg_group.paths })
    }
}
07070100000022000081A4000000000000000000000001647DB23000000224000000000000000000000000000000000000002E00000000evsieve-1.4.0~0/src/arguments/control_fifo.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::ArgumentError;
use crate::arguments::lib::ComplexArgGroup;

/// Represents a --control-fifo argument.
pub(super) struct ControlFifoArg {
    pub paths: Vec<String>,
}

impl ControlFifoArg {
	pub fn parse(args: Vec<String>) -> Result<ControlFifoArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &[],
            &[],
            true,
            false,
        )?;

        Ok(ControlFifoArg {
            paths: arg_group.paths
        })
    }
}07070100000023000081A4000000000000000000000001647DB23000000CF4000000000000000000000000000000000000002700000000evsieve-1.4.0~0/src/arguments/delay.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::ArgumentError;
use crate::arguments::lib::ComplexArgGroup;
use crate::key::{Key, KeyParser};
use crate::stream::delay::Delay;
use crate::time::Duration;

/// Represents a --delay argument.
pub(super) struct DelayArg {
    pub keys: Vec<Key>,
    pub period: Duration,
}

impl DelayArg {
	pub fn parse(args: Vec<String>) -> Result<DelayArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &[],
            &["period"],
            false,
            true,
        )?;

        let keys = KeyParser::default_filter()
            .parse_all(&arg_group.get_keys_or_empty_key())?;
        
        let period = parse_period_value(
            &arg_group.require_unique_clause("period")?
        )?;

        Ok(DelayArg { keys, period })
    }

    pub fn compile(self) -> Delay {
        Delay::new(self.keys, self.period)
    }
}

/// Parses a number of seconds with up to nanosecond precision.
pub fn parse_period_value(value: &str) -> Result<Duration, ArgumentError> {
    let first_token = match value.chars().next() {
        Some(token) => token,
        None => return Err(ArgumentError::new("Empty period specified.")),
    };
    if first_token == '-' {
        return Err(ArgumentError::new("The period must be nonnegative."));
    }

    let (before_decimal, after_decimal) = crate::utils::split_once(value, ".");
    let seconds = before_decimal.parse::<u64>().map_err(|_| ArgumentError::new(format!(
        "Cannot interpret {} as a number.", value,
    )))?;

    // A quick check to avoid cases of integer overflow.
    if seconds > 1_000_000_000 {
        return Err(ArgumentError::new(format!(
            "Period of {} seconds too long: unable to work with periods that are over a billion seconds.", seconds
        )))
    }

    // Compute the amount of nanoseconds after the period.
    let nanoseconds = match after_decimal {
        Some(string) => {
            let as_uint = string.parse::<u64>().map_err(|_| ArgumentError::new(format!(
                "Cannot interpret {} as a number.", value,
            )))?;
            let digits_after_period = string.len();
            if digits_after_period > 9 {
                return Err(ArgumentError::new("Cannot specify time periods with higher than nanosecond precision."));
            }
            as_uint * 10_u64.pow((9 - digits_after_period) as u32)
        },
        None => 0,
    };

    let total_nanoseconds: u64 = seconds * 1_000_000_000 + nanoseconds;
    if total_nanoseconds == 0 {
        return Err(ArgumentError::new("Cannot specify a period of zero."));
    }

    Ok(Duration::from_nanos(total_nanoseconds))
}

#[test]
fn unittest() {
    assert_eq!(parse_period_value("1").unwrap(), Duration::from_secs(1));
    assert_eq!(parse_period_value("5").unwrap(), Duration::from_secs(5));
    assert_eq!(parse_period_value("2.04").unwrap(), Duration::from_millis(2_040));
    assert_eq!(parse_period_value("0.049874").unwrap(), Duration::from_micros(49874));
    assert_eq!(parse_period_value("0.000082339").unwrap(), Duration::from_nanos(82339));
    parse_period_value("0.0000823391").unwrap_err();
    parse_period_value("0").unwrap_err();
    parse_period_value("0.0").unwrap_err();
    parse_period_value("-1").unwrap_err();
}07070100000024000081A4000000000000000000000001647DB23000002033000000000000000000000000000000000000002600000000evsieve-1.4.0~0/src/arguments/hook.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::ArgumentError;
use crate::utils;
use crate::state::{State, ToggleIndex};
use crate::stream::hook::{Effect, Trigger, EventDispatcher};
use crate::key::{Key, KeyParser};
use crate::event::Namespace;
use crate::arguments::lib::ComplexArgGroup;
use std::collections::HashMap;
use crate::time::Duration;

/// The KeyParser that is used to parse Hook keys.
pub(super) const PARSER: KeyParser = KeyParser {
    allow_transitions: false,
    allow_values: true,
    allow_ranges: true,
    allow_types: false,
    default_value: "1~",
    allow_relative_values: false,
    forbid_non_EV_KEY: false,
    namespace: Namespace::User,
};

/// Represents a --hook argument.
#[derive(Clone)]
pub(super) struct HookArg {
    /// The keys on which this --hook triggers and their original string representations.
    pub keys_and_str: Vec<(Key, String)>,

    pub exec_shell: Vec<String>,
    pub toggle_action: HookToggleAction,
    pub period: Option<Duration>,
    pub sequential: bool,
    /// Specified by the send-key clause. Whenever this hook is triggered, a kEY_DOWN
    /// of the following keys is sent, and a KEY_UP is sent when this hook is released.
    pub send_keys: Vec<Key>,
    /// Specified by the breaks-on clause. Whenever an event matches one of the following
    /// keys but not one of its keys_and_str, all trackers invalidate.
    pub breaks_on: Vec<Key>,
}

impl HookArg {
	pub fn parse(args: Vec<String>) -> Result<HookArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &["toggle", "sequential"],
            &["exec-shell", "toggle", "period", "send-key", "breaks-on"],
            false,
            true,
        )?;

        let toggle_action = HookToggleAction::parse(arg_group.has_flag("toggle"), arg_group.get_clauses("toggle"))?;
        let keys_str = arg_group.keys.clone();
        let keys = PARSER.parse_all(&keys_str)?;
        let keys_and_str = keys.into_iter().zip(keys_str).collect();

        let sequential = arg_group.has_flag("sequential");
        let period = match arg_group.get_unique_clause("period")? {
            None => None,
            Some(value) => Some(crate::arguments::delay::parse_period_value(&value)?),
        };

        let send_keys = KeyParser {
            allow_transitions: false,
            allow_values: false,
            allow_ranges: false,
            allow_types: false,
            default_value: "",
            allow_relative_values: false,
            forbid_non_EV_KEY: true,
            namespace: Namespace::User,
        }.parse_all(&arg_group.get_clauses("send-key"))?;

        let breaks_on = KeyParser::default_filter()
            .parse_all(&arg_group.get_clauses("breaks-on"))?;

        if arg_group.keys.is_empty() {
            Err(ArgumentError::new("A --hook argument requires at least one key."))
        } else {
            Ok(HookArg {
                keys_and_str,
                exec_shell: arg_group.get_clauses("exec-shell"),
                toggle_action, period, sequential, send_keys, breaks_on
            })
        }
    }

    pub fn compile_trigger(&self) -> Trigger {
        let keys: Vec<Key> = self.keys_and_str.iter().map(|(key, _)| key.clone()).collect();
        Trigger::new(keys, self.breaks_on.clone(), self.period, self.sequential)
    }

    pub fn compile_event_dispatcher(&self) -> EventDispatcher {
        EventDispatcher::from_send_keys(self.send_keys.clone())
    }
}

/// Represents how a single toggle clause on a hook should modify some toggle.
#[derive(Clone, Copy)]
enum HookToggleShift {
    /// Move the active index to the next one, wrapping around.
    Next,
    /// Set the active index to a specific index.
    ToIndex(usize),
}

/// Represents the aggregate effect of all toggle= clauses on a single --hook.
/// This is used to track arguments, this is not the implementation of such an effect.
#[derive(Clone)]
pub struct HookToggleAction {
    /// The action based on a toggle flag or a toggle= without id.
    global_action: Option<HookToggleShift>,
    /// The set of specific toggle=id:index specified.
    by_id_actions: HashMap<String, HookToggleShift>,
}

impl HookToggleAction {
    fn new() -> HookToggleAction {
        HookToggleAction {
            global_action: None,
            by_id_actions: HashMap::new(),
        }
    }

    pub fn parse(has_toggle_flag: bool, toggle_clauses: Vec<String>) -> Result<HookToggleAction, ArgumentError> {
        let mut toggle_action = HookToggleAction::new();
        if has_toggle_flag {
            toggle_action.global_action = Some(HookToggleShift::Next);
        }
        for clause in toggle_clauses {
            let (id, index_str_opt) = utils::split_once(&clause, ":");
            let index: HookToggleShift = match index_str_opt {
                None => HookToggleShift::Next,
                Some(index_str) => HookToggleShift::ToIndex(
                    match index_str.parse::<usize>() {
                        Ok(value) => match value {
                            0 => return Err(ArgumentError::new("Cannot use toggle index 0: toggle indices start at 1.")),
                            _ => value - 1,
                        },
                        Err(error) => return Err(ArgumentError::new(format!("Cannot interpret {} as an integer: {}.", index_str, error))),
                    }
                ),
            };
            match id {
                "" => match toggle_action.global_action {
                    None => { toggle_action.global_action = Some(index); },
                    Some(_) => return Err(ArgumentError::new("A --hook cannot have multiple unspecified toggle clauses.")),
                },
                _ => {
                    match toggle_action.by_id_actions.get(id) {
                        None => { toggle_action.by_id_actions.insert(id.to_owned(), index); },
                        Some(_) => return Err(ArgumentError::new(format!("A toggle={} clause has been specified multiple times.", {id}))),
                    }
                }
            }
        }

        Ok(toggle_action)
    }

    /// Returns a list of all toggle effects that a hook needs to implement this HookToggleAction.
    /// Requires a map mapping toggle's id to their index. This map must contain all toggles which
    /// have an ID, but does not need to contain toggles that don't have any ID.
    pub fn implement(&self, state: &State, toggle_index_by_id: &HashMap<String, ToggleIndex>) -> Result<Vec<Effect>, ArgumentError> {
        let mut effects: Vec<Effect> = Vec::new();
        let mut specified_indices: Vec<ToggleIndex> = Vec::new();
        for (toggle_id, &shift) in &self.by_id_actions {
            let toggle_index = *toggle_index_by_id.get(toggle_id).ok_or_else(|| {
                ArgumentError::new(format!("No toggle with the id \"{}\" exists.", toggle_id))
            })?;

            if let HookToggleShift::ToIndex(target_index) = shift {
                let toggle_size = state[toggle_index].size();
                if target_index >= toggle_size {
                    return Err(ArgumentError::new(format!(
                        "The index {} is out of range for the toggle with id \"{}\".", target_index + 1, toggle_id
                    )))
                }
            }

            specified_indices.push(toggle_index);
            effects.push(Box::new(move |state: &mut State| {
                match shift {
                    HookToggleShift::Next => state[toggle_index].advance(),
                    HookToggleShift::ToIndex(value) => state[toggle_index].set_value_wrapped(value),
                }
            }));
        }
        if let Some(shift) = self.global_action {
            effects.push(Box::new(move |state: &mut State| {
                let toggles_affected = state.get_toggles_except(&specified_indices);
                for toggle in toggles_affected {
                    match shift {
                        HookToggleShift::Next => toggle.advance(),
                        HookToggleShift::ToIndex(value) => toggle.set_value_wrapped(value),
                    }
                }
            }));
        }

        Ok(effects)
    }
}
07070100000025000081A4000000000000000000000001647DB23000000F14000000000000000000000000000000000000002700000000evsieve-1.4.0~0/src/arguments/input.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::domain;
use crate::domain::Domain;
use crate::predevice::{GrabMode, PersistMode};
use crate::error::ArgumentError;
use crate::arguments::lib::ComplexArgGroup;

/// Represents an --input argument.
pub(super) struct InputDevice {
    /// The domain of this input device.
    pub domain: Option<Domain>,
    /// All input device paths. If multiple are specified, it will read from multiple devices.
    /// At least one path must be specified.
	pub paths: Vec<String>,
    pub grab_mode: GrabMode,
    pub persist_mode: PersistMode,
}

impl InputDevice {
	pub fn parse(args: Vec<String>) -> Result<InputDevice, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &["grab"],
            &["domain", "grab", "persist"],
            true,
            false,
        )?;

        let domain = match arg_group.get_unique_clause("domain")? {
            None => None,
            Some(domain_str) => {
                let mut chars = domain_str.chars();
                let first_char: Option<char> = chars.next();
                let later_chars: String = chars.collect();
                match first_char {
                    Some('@') => return Err(ArgumentError::new(format!("There must be no @ in the domain name from \"domain={}\", because \"@{}\" represents a filter meaning \"any event with domain {}\". Try specifying \"domain={}\" instead.", domain_str, later_chars, later_chars, later_chars))),
                    None => return Err(ArgumentError::new("The domain= clause of an input argument cannot be empty.")),
                    _ => (),
                };
                Some(domain::resolve(&domain_str)?)
            }
        };

        let grab_mode = match arg_group.get_unique_clause_or_default_if_flag("grab", "auto")? {
            None => GrabMode::None,
            Some(value) => match value.as_str() {
                "auto" => GrabMode::Auto,
                "force" => GrabMode::Force,
                _ => return Err(ArgumentError::new("Invalid grab mode specified.")),
            }
        };

        let persist_mode = match arg_group.get_unique_clause("persist")? {
            None => PersistMode::None,
            Some(value) => match value.as_str() {
                "reopen" => PersistMode::Reopen,
                "none" => PersistMode::None,
                "exit" => PersistMode::Exit,
                _ => return Err(ArgumentError::new("Invalid persist mode specified.")),
            }
        };

        let paths = arg_group.require_paths()?;

        match persist_mode {
            PersistMode::None | PersistMode::Exit => {},
            PersistMode::Reopen => {
                if paths.iter().any(|path| is_direct_event_device(path)) {
                    println!("Warning: it is a bad idea to enable persistence on paths like /dev/input/event* because the kernel does not guarantee that the number of each event device remains constant. If such a device were to de disattached and reattached, it may show up under a different number. We recommend identifying event devices through their links in /dev/input/by-id/.");
                }
            }
        }

        Ok(InputDevice {
            domain, grab_mode, persist_mode, paths
        })
    }
}

/// Returns true if `path` is of the form `^/dev/input/event[0-9]+$`.
fn is_direct_event_device(path: &str) -> bool {
    let path = match path.strip_prefix("/dev/input/event") {
        Some(string) => string,
        None => return false,
    };

    path.chars().all(char::is_numeric)
}

#[test]
fn unittest() {
    assert!(is_direct_event_device("/dev/input/event1"));
    assert!(is_direct_event_device("/dev/input/event23"));
    assert!(! is_direct_event_device("/dev/input/by-id/event23"));
    assert!(! is_direct_event_device("/dev/input/event1foo"));
}07070100000026000081A4000000000000000000000001647DB2300000247C000000000000000000000000000000000000002500000000evsieve-1.4.0~0/src/arguments/lib.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::utils::split_once;
use crate::error::ArgumentError;
use std::path::{Path, PathBuf};

static DEV_ID_DIR: &str = "/dev/input/by-id";

/// A ComplexArgGroup represents a group like "--input /dev/keyboard domain=foo grab",
/// containing paths like "/dev/keyboard", flags like "grab" and clauses like "domain=foo".
///
/// Depending on the type of argument, some clauses may be specified multiple times,
/// other times there may be at most one of them.
pub(super) struct ComplexArgGroup {
    /// In the example, this would be "--input"
    pub name: String,                       
    /// In the example, this would be ["grab"]
    flags: Vec<String>,
    /// In the example, this would be [("domain", "foo")]
    clauses: Vec<(String, String)>,

    /// Any stray keys that show up in the argument list.
    pub keys: Vec<String>,
    pub paths: Vec<String>,
}

impl ComplexArgGroup {
    /// Args must include the name of this ArgGroup, e.g. "--input".
    ///
    /// # Panics
    ///
    /// Panics when args is empty.
    pub fn parse(args: Vec<String>,
            supported_flags: &[&str],
            supported_clauses: &[&str],
            supports_paths: bool,
            supports_keys: bool) -> Result<ComplexArgGroup, ArgumentError> {
        
        let mut args_iter = args.into_iter();
        let arg_name = args_iter.next().expect("Internal error: created an argument group out of no arguments.");

        let mut flags: Vec<String> = Vec::new();
        let mut clauses: Vec<(String, String)> = Vec::new();
        let mut keys: Vec<String> = Vec::new();
        let mut paths: Vec<String> = Vec::new();
    
        for arg in args_iter {
            // Check whether this argument is a path.
            if is_path(&arg) {
                if supports_paths {
                    paths.push(arg);
                    continue;
                } else {
                    return Err(ArgumentError::new(format!(
                        "The {} argument doesn't take any paths: \"{}\"", arg_name, arg
                    )))
                }
            }

            // Check whether this argument is a key.
            if crate::key::resembles_key(&arg) {
                if supports_keys {
                    keys.push(arg);
                    continue;
                } else {
                    return Err(ArgumentError::new(format!(
                        "The {} argument doesn't take any keys: \"{}\"", arg_name, arg
                    )))
                }
            }

            // Check whether this argument is a flag or clause.
            let (name, value_opt) = split_once(&arg, "=");
            let name = name.to_string();

            // Check if it's a clause.
            if let Some(value) = value_opt {
                if supported_clauses.contains(&name.as_str()) {
                    clauses.push((name.to_string(), value.to_string()));
                    continue;
                } else {
                    return Err(ArgumentError::new(
                        match supported_flags.contains(&name.as_str()) {
                            true => format!("The {} argument's {} flag doesn't accept a value. Try removing the  \"={}\" part.", arg_name, name, value),
                            false => format!("The {} argument doesn't accept a {} clause: \"{}\"", arg_name, name, arg),
                        }
                    ));
                }
            }

            // Check is it's a flag.
            if supported_flags.contains(&name.as_str()) {
                if ! flags.contains(&name) {
                    flags.push(name);
                    continue;
                } else {
                    return Err(ArgumentError::new(format!(
                        "The {} flag has been provided multiple times.", name
                    )))
                }
            }

            // If we reach this point, the argument is invalid.
            // Try to diagnose what went wrong to give the most helpful error message possible.

            // Check if it is a clause that doesn't have a value provided.
            if supported_clauses.contains(&name.as_str()) {
                return Err(ArgumentError::new(format!("The {} argument's {} clause requires some value: \"{}=something\".", arg_name, name, name)));
            }

            // Check if it is a path in nonabsolute form.
            if let Some(absolute_path) = resembles_nonabsolute_path(&arg) {
                if supports_paths {
                    return Err(ArgumentError::new(format!(
                        "The \"{}\" flag looks like it is a path. Paths must be provided in absolute form starting with a /. Try providing \"{}\" instead.",
                        arg, absolute_path.display()
                    )))
                } else {
                    return Err(ArgumentError::new(format!(
                        "The \"{}\" flag looks like it is a path. The {} argument doesn't take any paths.",
                        arg, arg_name
                    )))
                }
            }

            // Return a generic error.
            return Err(ArgumentError::new(format!("The {} argument doesn't take a {} flag.", arg_name, name)));
        }

        Ok(ComplexArgGroup {
            name: arg_name, flags, clauses, keys, paths,
        })
    }

    pub fn has_flag(&self, name: &str) -> bool {
        self.flags.contains(&name.to_owned())
    }

    pub fn get_clauses(&self, name: &str) -> Vec<String> {
        self.clauses.iter().cloned().filter_map(|(clause_name, value)| {
            if name == clause_name {
                Some(value)
            } else {
                None
            }
        }).collect()
    }

    /// Get a clause of which at most one may exist.
    /// Multiple copies of this clause will return an error, zero copies will return None.
    pub fn get_unique_clause(&self, name: &str) -> Result<Option<String>, ArgumentError> {
        let clauses = self.get_clauses(name);
        match clauses.len() {
            1 => Ok(Some(clauses[0].clone())),
            0 => Ok(None),
            _ => Err(ArgumentError::new(format!(
                "Cannot provide multiple copies of the {}= clause to {}.", name, self.name
            ))),
        }
    }

    /// Gets a clause of which exacly one must exist.
    pub fn require_unique_clause(&self, name: &str) -> Result<String, ArgumentError> {
        let clause = self.get_unique_clause(name)?;
        match clause {
            Some(clause) => Ok(clause),
            None => Err(ArgumentError::new(format!(
                "The {} argument requires a {}= clause.", self.name, name
            ))),
        }
    }

    pub fn require_paths(&self) -> Result<Vec<String>, ArgumentError> {
        match self.paths.len() {
            0 => Err(ArgumentError::new(format!(
                "The {} argument requires a path. Remember that all paths must be provided as absolute paths.", self.name,
            ))),
            _ => Ok(self.paths.clone()),
        }
    }

    pub fn require_keys(&self) -> Result<Vec<String>, ArgumentError> {
        match self.keys.len() {
            0 => Err(ArgumentError::new(format!(
                "The {} argument requires a key.", self.name,
            ))),
            _ => Ok(self.keys.clone()),
        }
    }

    /// Returns all keys this ComplexArgGroup has. If it has no keys, it will return
    /// a single "" key instead.
    pub fn get_keys_or_empty_key(&self) -> Vec<String> {
        match self.keys.len() {
            0 => vec!["".to_string()],
            _ => self.keys.clone(),
        }
    }

    /// Returns the value of the given clause. If no such clause is specified but a flag with
    /// the same name is specified, returns the value of `default_if_flag`.
    pub fn get_unique_clause_or_default_if_flag(&self, clause_or_flag: &str, default_if_flag: &str) -> Result<Option<String>, ArgumentError> {
        if self.has_flag(clause_or_flag) && ! self.get_clauses(clause_or_flag).is_empty() {
            return Err(ArgumentError::new(format!(
                "Cannot specify both the {} flag an a {} clause.", clause_or_flag, clause_or_flag
            )));
        }
        Ok(match self.get_unique_clause(clause_or_flag)? {
            Some(value) => Some(value),
            None => match self.has_flag(clause_or_flag) {
                true => Some(default_if_flag.into()),
                false => None,
            },
        })
    }
}

pub(super) fn is_path(path: &str) -> bool {
    path.starts_with('/')
}

/// Checks if arg seems to be a path that is not provided in absolute form. If it is, returns
/// the path in absolute form. Otherwise, returns None.
fn resembles_nonabsolute_path(arg: &str) -> Option<PathBuf> {
    let current_dir_opt = std::env::current_dir();
    let starting_points: Vec<&Path> = match current_dir_opt.as_ref() {
        Ok(current_dir) => vec![Path::new(DEV_ID_DIR), Path::new(current_dir)],
        Err(_) => vec![Path::new(DEV_ID_DIR)],
    };

    for starting_point in starting_points {
        let path_from_starting_point = starting_point.join(arg);
        if ! path_from_starting_point.exists() {
            continue;
        }

        return Some(path_from_starting_point)
    }

    None
}07070100000027000081A4000000000000000000000001647DB23000000754000000000000000000000000000000000000002500000000evsieve-1.4.0~0/src/arguments/map.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::{ArgumentError, InternalError, RuntimeError};
use crate::arguments::lib::ComplexArgGroup;
use crate::key::{Key, KeyParser};
use crate::event::Namespace;

/// Represents a --map or --copy argument.
pub(super) struct MapArg {
	pub input_key: Key,
    pub output_keys: Vec<Key>,
}

impl MapArg {
	pub fn parse(args: Vec<String>) -> Result<MapArg, RuntimeError> {
        let arg_group = ComplexArgGroup::parse(args,
            &["yield"],
            &[],
            false,
            true,
        )?;

        let copy = match arg_group.name.as_str() {
            "--copy" => true,
            "--map" => false,
            _ => return Err(InternalError::new("A map has been constructed from neither a --map or a --copy.").into()),
        };

        // Parse the keys.
        let keys_str = arg_group.require_keys()?;
        let input_key = KeyParser::default_filter().parse(&keys_str[0])?;
        
        let output_namespace = match arg_group.has_flag("yield") {
            true => Namespace::Yielded,
            false => Namespace::User,
        };
        let mut output_keys = KeyParser::default_mask()
            .with_namespace(output_namespace)
            .parse_all(&keys_str[1..])?;

        if copy {
            output_keys.insert(0, Key::copy());
        }
        
        Ok(MapArg {
            input_key, output_keys,
        })
    }
}

/// Represents a --block argument.
pub(super) struct BlockArg {
	pub keys: Vec<Key>,
}

impl BlockArg {
	pub fn parse(args: Vec<String>) -> Result<BlockArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &[],
            &[],
            false,
            true,
        )?;

        let keys = KeyParser::default_filter().parse_all(&arg_group.get_keys_or_empty_key())?;

        Ok(BlockArg { keys })
    }
}07070100000028000081A4000000000000000000000001647DB230000008B5000000000000000000000000000000000000002700000000evsieve-1.4.0~0/src/arguments/merge.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::ArgumentError;
use crate::arguments::lib::ComplexArgGroup;
use crate::key::{Key, KeyParser};
use crate::stream::merge::Merge;

/// Represents a --merge argument.
pub(super) struct MergeArg {
    pub keys: Vec<Key>,
}

impl MergeArg {
	pub fn parse(args: Vec<String>) -> Result<MergeArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &[],
            &[],
            false,
            true,
        )?;

        let parser = KeyParser {
            default_value: "",
            allow_values: false,
            allow_ranges: false,
            allow_transitions: false,
            allow_types: true,
            allow_relative_values: false,
            forbid_non_EV_KEY: true,
            namespace: crate::event::Namespace::User,
        };

        let keys: Vec<Key> = parser.parse_all(&arg_group.get_keys_or_empty_key())?;

        Ok(MergeArg { keys })
    }

    pub fn compile(self) -> Merge {
        Merge::new(self.keys)
    }
}

#[test]
fn unittest() {
    assert!(MergeArg::parse(vec!["--merge".to_string()]).is_ok());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "".to_string()]).is_ok());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "key".to_string()]).is_ok());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "key:a".to_string()]).is_ok());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "btn".to_string()]).is_ok());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "btn:left".to_string()]).is_ok());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "key".to_string(), "btn".to_string()]).is_ok());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "@foo".to_string()]).is_ok());

    assert!(MergeArg::parse(vec!["--merge".to_string(), "key:a:1".to_string()]).is_err());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "abs".to_string()]).is_err());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "abs:x".to_string()]).is_err());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "key".to_string(), "abs".to_string()]).is_err());
    assert!(MergeArg::parse(vec!["--merge".to_string(), "abs@foo".to_string()]).is_err());
}07070100000029000081A4000000000000000000000001647DB2300000079D000000000000000000000000000000000000002800000000evsieve-1.4.0~0/src/arguments/output.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::predevice::RepeatMode;
use crate::error::ArgumentError;
use crate::arguments::lib::ComplexArgGroup;
use crate::key::{Key, KeyParser};
use crate::event::Namespace;
use std::path::PathBuf;

const DEFAULT_NAME: &str = "Evsieve Virtual Device";

pub(super) struct OutputDevice {
    pub create_link: Option<PathBuf>,
    pub name: String,
    pub keys: Vec<Key>,
    pub repeat_mode: RepeatMode,
}

impl OutputDevice {
	pub fn parse(args: Vec<String>) -> Result<OutputDevice, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &["repeat"],
            &["create-link", "name", "repeat"],
            false,
            true,
        )?;

        let repeat_mode = match arg_group.get_unique_clause_or_default_if_flag("repeat", "enable")? {
            None => RepeatMode::Passive,
            Some(mode) => match mode.as_str() {
                "enable" => RepeatMode::Enable,
                "disable" => RepeatMode::Disable,
                "passive" => RepeatMode::Passive,
                _ => return Err(ArgumentError::new(format!("Invalid repeat mode \"{}\".", mode)))
            },
        };

        let name = arg_group.get_unique_clause("name")?.unwrap_or_else(|| DEFAULT_NAME.to_owned());
        if name.is_empty() {
            return Err(ArgumentError::new("Output device name cannot be empty."));
        }

        // Parse the keys that shall be sent to this output device.
        let key_strs = arg_group.get_keys_or_empty_key();
        let mut keys = Vec::new();
        for &namespace in &[Namespace::User, Namespace::Yielded] {
            keys.append(
                &mut KeyParser::default_filter().with_namespace(namespace).parse_all(&key_strs)?
            );
        }

		Ok(OutputDevice {
            create_link: arg_group.get_unique_clause("create-link")?.map(PathBuf::from),
            name, keys, repeat_mode,
        })
    }
}0707010000002A000081A4000000000000000000000001647DB23000004509000000000000000000000000000000000000002800000000evsieve-1.4.0~0/src/arguments/parser.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::domain;
use crate::error::{ArgumentError, RuntimeError, Context, SystemError};
use crate::key::Key;
use crate::event::Namespace;
use crate::stream::hook::Hook;
use crate::stream::map::{Map, Toggle};
use crate::stream::withhold::Withhold;
use crate::stream::{StreamEntry, Setup};
use crate::predevice::{PreInputDevice, PreOutputDevice};
use crate::state::{State, ToggleIndex};
use crate::control_fifo::ControlFifo;
use crate::arguments::hook::HookArg;
use crate::arguments::input::InputDevice;
use crate::arguments::output::OutputDevice;
use crate::arguments::toggle::ToggleArg;
use crate::arguments::map::{MapArg, BlockArg};
use crate::arguments::print::PrintArg;
use crate::arguments::delay::DelayArg;
use crate::arguments::withhold::WithholdArg;
use crate::arguments::control_fifo::ControlFifoArg;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

use super::config::ConfigArg;
use super::merge::MergeArg;

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");

/// Returns the help message that should be printed for the --help argument.
fn get_usage_msg() -> String {
    let mut result =
"Usage: evsieve [--input PATH... [domain=DOMAIN] [grab[=auto|force]] [persist=none|reopen|exit]]...
               [--map SOURCE [DEST...] [yield]]...
               [--copy SOURCE [DEST...] [yield]]...
               [--block [SOURCE...]]...
               [--toggle SOURCE DEST... [id=ID] [mode=consistent|passive]]...
               [--hook KEY... [exec-shell=COMMAND]... [toggle[=[ID][:INDEX]]]... [sequential] [period=SECONDS] [send-key=KEY]... [breaks-on=KEY]...]...
               [--withhold [KEY...]]...
               [--merge [EVENTS...]]...
               [--print [EVENTS...] [format=default|direct]]...
               [--delay [EVENTS...] period=SECONDS]...
               [--output [EVENTS...] [create-link=PATH] [name=NAME] [repeat[=MODE]]]...".to_owned();

    if cfg!(feature = "control-fifo") {
        result += "
               [--control-fifo PATH...]..."
    }
    if cfg!(feature = "config") {
        result += "
               [--config PATH...]..."
    }
                    

    result
}

/// Represents all arguments an user may pass to the evsieve program, except for
/// some special arguments like --config that may represent several other arguments.
enum Argument {
    InputDevice(InputDevice),
    OutputDevice(OutputDevice),
    MapArg(MapArg),
    HookArg(HookArg),
    BlockArg(BlockArg),
    ToggleArg(ToggleArg),
    PrintArg(PrintArg),
    MergeArg(MergeArg),
    DelayArg(DelayArg),
    WithholdArg(WithholdArg),
    ControlFifoArg(ControlFifoArg),
}

/// The MetaArgument represents things that may get turned into common arguments.
/// For example, the --config argument gets replaced by multiple other arguments
/// at an early stage in the parsing.
/// 
/// This distinction between Argument and MetaArgument helps us to be sure that
/// no unhandled meta-arguments are left during later stages of parsing.
enum MetaArgument {
    Common(Argument),
    ConfigArg(ConfigArg),
}

impl Argument {
    fn parse(args: Vec<String>) -> Result<Argument, RuntimeError> {
        let first_arg = &args[0];
        match first_arg.as_str() {
            "--input" => Ok(Argument::InputDevice(InputDevice::parse(args)?)),
            "--output" => Ok(Argument::OutputDevice(OutputDevice::parse(args)?)),
            "--map" => Ok(Argument::MapArg(MapArg::parse(args)?)),
            "--copy" => Ok(Argument::MapArg(MapArg::parse(args)?)),
            "--hook" => Ok(Argument::HookArg(HookArg::parse(args)?)),
            "--toggle" => Ok(Argument::ToggleArg(ToggleArg::parse(args)?)),
            "--block" => Ok(Argument::BlockArg(BlockArg::parse(args)?)),
            "--print" => Ok(Argument::PrintArg(PrintArg::parse(args)?)),
            "--merge" => Ok(Argument::MergeArg(MergeArg::parse(args)?)),
            "--delay" => Ok(Argument::DelayArg(DelayArg::parse(args)?)),
            "--withhold" => Ok(Argument::WithholdArg(WithholdArg::parse(args)?)),
            "--control-fifo" => {
                if cfg!(feature = "control-fifo") {
                    Ok(Argument::ControlFifoArg(ControlFifoArg::parse(args)?))
                } else {
                    Err(ArgumentError::new("The --control-fifo argument is not stabilized yet. This version of evsieve was compiled without support for --control-fifo.").into())
                }
            },
            _ => Err(ArgumentError::new(format!("Encountered unknown argument: {}", first_arg)).into()),
        }
    }
}

impl MetaArgument {
    fn parse(args: Vec<String>) -> Result<MetaArgument, RuntimeError> {
        match args[0].as_str() {
            "--config" => {
                if cfg!(feature = "config") {
                    Ok(MetaArgument::ConfigArg(ConfigArg::parse(args)?))
                } else {
                    Err(ArgumentError::new("The --config argument is not stabilized yet. This version of evsieve was compiled without support for --config.").into())
                }
            },
            _ => Argument::parse(args).map(MetaArgument::Common),
        }
    }
}

/// If a --version or --help or something is specified, prints a helpful message.
/// Returns true if --version or --help was requested, otherwise returns false.
pub fn check_help_and_version(args: &[String]) -> bool {
    if args.is_empty() // No args (program name was skipped)
            || args.contains(&"-?".to_owned())
            || args.contains(&"-h".to_owned())
            || args.contains(&"--help".to_owned()) {
        println!("{}", get_usage_msg());
        return true;
    }

    if args.contains(&"--version".to_owned()) {
        let version = VERSION.unwrap_or("unknown");
        println!("{}", version);
        return true;
    }

    false
}

/// Sorts arguments like ["--input", "/dev/foo", "--map", "key:a", "key:b"] into groups like
///     [["--input", "/dev/foo"], ["--map", "key:a", "key:b"]]
/// and uses the appropriate MetaArgument to represent each group.
fn sort_into_groups(args: Vec<String>) -> Result<Vec<MetaArgument>, RuntimeError> {
    let mut groups: Vec<Vec<String>> = Vec::new();
    let mut args_iter = args.into_iter().peekable();
	while let Some(first_arg) = args_iter.next() {
		if ! first_arg.starts_with("--") {
			return Err(ArgumentError::new(format!(
                "Expected an argument starting with --, encountered \"{}\".", first_arg
            )).into());
        }

        // Take items from the arg list until we encounter the start of the next argument.
        let mut new_group: Vec<String> = vec![first_arg];
        while let Some(next_arg) = args_iter.peek() {
            if next_arg.starts_with("--") {
                break;
            }
            new_group.push(args_iter.next().unwrap());
        }
		
		groups.push(new_group);
    }

    groups.into_iter().map(
        |group| MetaArgument::parse(group.clone()).with_context_of(|| format!(
            "While parsing the arguments \"{}\":", group.join(" ")
        )
    )).collect()
}

/// Sorts arguments that are strings into argument groups, then replaces all --config
/// arguments with the contents of their files and sorts those as well, recursively.
fn sort_and_expand_config(
    args_to_sort: Vec<String>,
    output_buffer: &mut Vec<Argument>,
    visited_config_files: Vec<&str>,
) -> Result<(), RuntimeError> {
    let meta_args = sort_into_groups(args_to_sort)?;

    for meta_arg in meta_args {
        match meta_arg {
            MetaArgument::Common(arg) => output_buffer.push(arg),
            MetaArgument::ConfigArg(config) => {
                for path in config.paths {
                    if visited_config_files.contains(&path.as_str()) {
                        return Err(ArgumentError::new(
                            format!("The configuration file {} is getting recursively included.", path)
                        ).into());
                    }
                    let file_content = std::fs::read_to_string(&path)
                        .map_err(SystemError::from)
                        .with_context_of(|| format!("While trying to read the file {}:", &path))?;

                    let file_args = crate::utils::shelllex::lex(&file_content)
                        .with_context_of(|| format!("While parsing the configuration file {}:", &path))?;

                    let mut local_visited_config_files = visited_config_files.clone();
                    local_visited_config_files.push(&path);

                    sort_and_expand_config(
                        file_args, output_buffer, local_visited_config_files
                    ).with_context_of(|| format!("While interpreting the configuration file {}:", &path))?
                }
            }
        }
    }

    Ok(())
}

fn parse(args: Vec<String>) -> Result<Vec<Argument>, RuntimeError> {
    let mut output: Vec<Argument> = Vec::new();
    sort_and_expand_config(args, &mut output, Vec::new())?;
    Ok(output)
}

pub struct Implementation {
    pub setup: Setup,
    pub input_devices: Vec<crate::io::input::InputDevice>,
    pub control_fifos: Vec<ControlFifo>,
}

/// This function does most of the work of turning the input arguments into the components of a
/// runnable program.
pub fn implement(args_str: Vec<String>)
        -> Result<Implementation, RuntimeError>
{
    let mut args: Vec<Argument> = parse(args_str)?;
    let mut input_devices: Vec<PreInputDevice> = Vec::new();
    let mut output_devices: Vec<PreOutputDevice> = Vec::new();
    let mut control_fifo_paths: Vec<String> = Vec::new();
    let mut stream: Vec<StreamEntry> = Vec::new();

    let mut state: State = State::new();

    // Maps a toggle's ID to the index at which it can be found.
    let mut toggle_indices: HashMap<String, ToggleIndex> = HashMap::new();

    // Reserve toggle indices ahead of time so --hooks can act upon indices of toggles
    // that will only be defined later.
    for arg in &args {
        if let Argument::ToggleArg(toggle_arg) = arg {
            if let Some(id) = toggle_arg.id.clone() {
                match toggle_indices.get(&id) {
                    Some(_) => {
                        return Err(ArgumentError::new("Two toggles cannot have the same id.").into());
                    },
                    None => {
                        let index = state.create_toggle_with_size(toggle_arg.size())?;
                        toggle_indices.insert(id, index);
                    }
                }
            }
        }
    }

    // Associate the --withhold argument with all --hook arguments before it.
    let mut consecutive_hooks: Vec<&mut HookArg> = Vec::new();
    for arg in &mut args {
        match arg {
            Argument::HookArg(hook_arg) => consecutive_hooks.push(hook_arg),
            Argument::WithholdArg(withhold_arg) => {
                withhold_arg.associate_hooks(&mut consecutive_hooks)
                    .with_context("While linking the --withhold arguments to their preceding hooks:")?;
                consecutive_hooks.clear();
            },
            _ => consecutive_hooks.clear(),
        }
    }

    // Keep track of the real paths for the input devices we've opened so we don't open the same
    // one twice.
    let mut input_device_real_paths: HashSet<PathBuf> = HashSet::new();

    // Construct the stream.
    for arg in args {
        match arg {
            Argument::InputDevice(device) => {
                for path_str in &device.paths {
                    let path: PathBuf = path_str.into();
                    let real_path = std::fs::canonicalize(path.clone()).map_err(
                        |_| ArgumentError::new(format!("The input device \"{}\" does not exist.", path_str))
                    )?;

                    // Opening the same device multiple times could spell trouble for certain
                    // possible future features and has little purpose, so we don't allow it.
                    if input_device_real_paths.contains(&real_path) {
                        return Err(ArgumentError::new(format!("The input device \"{}\" has been opened multiple times.", path_str)).into());
                    } else {
                        input_device_real_paths.insert(real_path);
                    }
                    
                    let source_domain = domain::get_unique_domain();
                    let target_domain = match &device.domain {
                        Some(value) => *value,
                        None => domain::resolve(path_str)?,
                    };

                    let input_device = PreInputDevice {
                        path, domain: source_domain,
                        grab_mode: device.grab_mode,
                        persist_mode: device.persist_mode,
                    };

                    // Register this device for later creation.
                    input_devices.push(input_device);
                    // Create a map to put those events into the stream at the right time.
                    stream.push(StreamEntry::Map(
                        Map::domain_shift(
                            source_domain, Namespace::Input,
                            target_domain, Namespace::User,
                        )
                    ));
                }
            },
            Argument::OutputDevice(device) => {
                // Create the output device.
                let target_domain = domain::get_unique_domain();
                let output_device = PreOutputDevice {
                    domain: target_domain,
                    create_link: device.create_link,
                    name: device.name,
                    repeat_mode: device.repeat_mode,
                };
                output_devices.push(output_device);
                
                // Map the keys to this output device.
                for key in device.keys {
                    let map = Map::new(
                        key,
                        vec![Key::from_domain_and_namespace(target_domain, Namespace::Output)],
                    );
                    stream.push(StreamEntry::Map(map));
                }
            },
            Argument::MapArg(map_arg) => {
                let map = Map::new(map_arg.input_key, map_arg.output_keys);
                stream.push(StreamEntry::Map(map));
            },
            Argument::BlockArg(block_arg) => {
                for key in block_arg.keys {
                    stream.push(StreamEntry::Map(Map::block(key)));
                }
            },
            Argument::HookArg(hook_arg) => {
                let mut hook = Hook::new(
                    hook_arg.compile_trigger(),
                    hook_arg.compile_event_dispatcher(),
                );

                for exec_shell in hook_arg.exec_shell {
                    hook.add_command("/bin/sh".to_owned(), vec!["-c".to_owned(), exec_shell]);
                }

                for effect in hook_arg.toggle_action.implement(&state, &toggle_indices)? {
                    hook.add_effect(effect);
                }
                
                stream.push(StreamEntry::Hook(hook));
            },
            Argument::WithholdArg(withhold_arg) => {
                stream.push(StreamEntry::Withhold(
                    Withhold::new(withhold_arg.keys, withhold_arg.associated_triggers)
                ));
            },
            Argument::ToggleArg(toggle_arg) => {
                let index = match &toggle_arg.id {
                    Some(id) => toggle_indices.get(id).cloned(),
                    None => None,
                };
                let toggle = Toggle::new(toggle_arg.input_key, toggle_arg.output_keys, toggle_arg.mode, &mut state, index)?;
                stream.push(StreamEntry::Toggle(toggle));
            },
            Argument::PrintArg(print_arg) => {
                stream.push(StreamEntry::Print(print_arg.compile()));
            },
            Argument::MergeArg(merge_arg) => {
                stream.push(StreamEntry::Merge(merge_arg.compile()));
            },
            Argument::DelayArg(delay_arg) => {
                stream.push(StreamEntry::Delay(delay_arg.compile()));
            },
            Argument::ControlFifoArg(control_fifo) => {
                control_fifo_paths.extend(control_fifo.paths);
            },
        }
    }

    // Do sanity checks.
    if ! are_unique(output_devices.iter().filter_map(|device| device.create_link.as_ref())) {
        return Err(ArgumentError::new("Multiple output devices cannot create a link at the same location.".to_owned()).into());
    }
    if ! are_unique(control_fifo_paths.iter()) {
        return Err(ArgumentError::new("A control fifo was specified twice at the same location.".to_owned()).into());
    }

    let control_fifos: Vec<ControlFifo> = control_fifo_paths.into_iter()
        .map(ControlFifo::create)
        .collect::<Result<Vec<ControlFifo>, SystemError>>()?;

    // Compute the capabilities of the output devices.
    let (input_devices, input_capabilities) = crate::io::input::open_and_query_capabilities(input_devices)?;
    let setup = Setup::create(stream, output_devices, state, toggle_indices, input_capabilities)?;

    Ok(Implementation { setup, input_devices, control_fifos })
}

/// Returns true if all items in the iterator are unique, otherwise returns false.
fn are_unique<T: Eq>(items: impl Iterator<Item=T>) -> bool {
    let mut seen_items = Vec::new();
    for item in items {
        if seen_items.contains(&item) {
            return false
        }
        seen_items.push(item)
    }
    true
}0707010000002B000081A4000000000000000000000001647DB230000004B5000000000000000000000000000000000000002700000000evsieve-1.4.0~0/src/arguments/print.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::ArgumentError;
use crate::arguments::lib::ComplexArgGroup;
use crate::key::{Key, KeyParser};
use crate::stream::print::{EventPrinter, EventPrintMode};

/// Represents a --print argument.
pub(super) struct PrintArg {
    pub keys: Vec<Key>,
    pub mode: EventPrintMode,
}

impl PrintArg {
	pub fn parse(args: Vec<String>) -> Result<PrintArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &[],
            &["format"],
            false,
            true,
        )?;

        let keys = KeyParser::default_filter().parse_all(&arg_group.get_keys_or_empty_key())?;

        let mode = match arg_group.get_unique_clause("format")? {
            Some(value) => match value.as_str() {
                "direct" => EventPrintMode::Direct,
                "default" => EventPrintMode::Detailed,
                other => return Err(ArgumentError::new(format!("Invalid --print format: {}", other))),
            } ,
            None => EventPrintMode::Detailed,
        };

        Ok(PrintArg { keys, mode })
    }

    pub fn compile(self) -> EventPrinter {
        EventPrinter::new(self.keys, self.mode)
    }
}0707010000002C000081A4000000000000000000000001647DB23000000DDF000000000000000000000000000000000000002600000000evsieve-1.4.0~0/src/arguments/test.rs// For some reason the compiler sees functions that are only used in unittests as dead code.
#![allow(dead_code)]

use crate::error::RuntimeError;

#[test]
fn test_argument_validity() {
    // Test key format.
    require_ok( ["--map", ""]);
    require_ok( ["--map", "key"]);
    require_ok( ["--map", "rel"]);
    require_err(["--map", "quux"]);
    require_ok( ["--map", "key:a"]);
    require_err(["--map", "key:quux"]);
    require_ok( ["--map", "key:a:1"]);
    require_err(["--map", "key:a:1:2"]);
    require_ok( ["--map", "::1"]);
    require_ok( ["--map", "::1", "::2"]);
    require_err(["--map", "::1", "key::2"]);
    require_err(["--map", "key:a:quux"]);
    require_ok( ["--map", "key:a:1~"]);
    require_ok( ["--map", "key:a:~1"]);
    require_ok( ["--map", "key:a:1~2"]);
    require_ok( ["--map", "key:a:1..2"]);
    require_ok( ["--map", "key:a:1~2..1~2"]);

    require_ok( ["--map", "", ""]);
    require_err(["--map", "", "key"]);
    require_ok( ["--map", "", "key:a"]);
    require_ok( ["--map", "", "key:a:1"]);
    require_err(["--map", "", "key:a:1..2"]);

    require_ok( ["--map", "rel:x", "rel:x:x"]);
    require_ok( ["--map", "rel:x", "rel:x:-x"]);
    require_ok( ["--map", "rel:x", "rel:x:1.4x"]);
    require_ok( ["--map", "rel:x", "rel:x:2d"]);
    require_ok( ["--map", "rel:x", "rel:x:-d"]);
    require_ok( ["--map", "rel:x", "rel:x:x-d"]);
    require_ok( ["--map", "rel:x", "rel:x:x+1"]);
    require_ok( ["--map", "rel:x", "rel:x:1+x"]);
    require_err(["--map", "rel:x", "rel:x:xd"]);
    require_err(["--map", "rel:x:x", "rel:x:1"]);
    require_err(["--map", "rel:x:d", "rel:x:1"]);
    
    require_ok( ["--map", "rel:x", "rel:x:1+d"]);
    require_ok( ["--map", "rel:x", "rel:x:1+2d"]);
    require_ok( ["--map", "rel:x", "rel:x:1+0d"]);
    require_ok( ["--map", "rel:x", "rel:x:0x"]);
    
    require_err(["--map", "rel:x", "rel:x:1.2"]);
    require_err(["--map", "rel:x", "rel:x:1.0"]);
    require_err(["--map", "rel:x", "rel:x:1.0+x"]);

    require_err(["--map", "key:"]);
    require_err(["--map", "key::"]);
    require_err(["--map", "key:a:"]);
    
    // Test --withhold.
    require_ok( ["--hook", "key:a", "--withhold"]);
    require_err(["--hook", "abs:x", "--withhold"]);
    require_err(["--hook", "key:a:1~", "--withhold"]);
    require_err(["--hook", "", "--withhold"]);
    require_err(["--hook", "--withhold", "key"]);
    require_err(["--hook", "@foo", "--withhold", "key"]);
    require_err(["--hook", "abs:x", "--hook", "key:a", "--withhold"]);
    require_err(["--hook", "key:a", "--hook", "abs:x", "--withhold"]);
    require_err(["--hook", "key:a", "abs:x", "--withhold"]);
    require_ok( ["--hook", "key:a", "abs:x", "--withhold", "key"]);
    require_ok( ["--hook", "key:a", "key:b", "--withhold"]);
    require_err(["--hook", "key:a", "key:b:1", "--withhold"]);
    require_err(["--hook", "key:a", "key:b:1", "--withhold", "key"]);
    require_ok( ["--hook", "key:a", "key:b:1", "--withhold", "key:a"]);
    require_ok( ["--hook", "key:a", "key:b:1", "--withhold", "btn"]);
}

fn require_ok(args: impl IntoIterator<Item=impl Into<String>>) {
    try_implement(args).unwrap();
}

fn require_err(args: impl IntoIterator<Item=impl Into<String>>) {
    assert!(try_implement(args).is_err());
}

fn try_implement(args: impl IntoIterator<Item=impl Into<String>>) -> Result<crate::arguments::parser::Implementation, RuntimeError> {
    let args: Vec<String> =
        args.into_iter().map(|item| item.into())
        .collect();

    crate::arguments::parser::implement(args)
}0707010000002D000081A4000000000000000000000001647DB2300000076D000000000000000000000000000000000000002800000000evsieve-1.4.0~0/src/arguments/toggle.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::ArgumentError;
use crate::arguments::lib::ComplexArgGroup;
use crate::key::{Key, KeyParser};
use crate::stream::map::ToggleMode;

/// Represents a --toggle argument.
pub(super) struct ToggleArg {
	pub input_key: Key,
    pub output_keys: Vec<Key>,
    pub id: Option<String>,
    pub mode: ToggleMode,
}

impl ToggleArg {
	pub fn parse(args: Vec<String>) -> Result<ToggleArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &[],
            &["id", "mode"],
            false,
            true,
        )?;

        let mode = match arg_group.get_unique_clause("mode")? {
            None => ToggleMode::Consistent,
            Some(mode_str) => match mode_str.as_str() {
                "consistent" => ToggleMode::Consistent,
                "passive" => ToggleMode::Passive,
                _ => return Err(ArgumentError::new(
                    format!("Invalid toggle mode specified: {}", mode_str)
                ))
            }
        };

        let keys = arg_group.require_keys()?;
        if keys.len() < 2 {
            return Err(ArgumentError::new("A --toggle argument requires an input key and at least one output key."));
        }

        let input_key = KeyParser::default_filter().parse(&keys[0])?;
        let output_keys = KeyParser::default_mask().parse_all(&keys[1..])?;

        let id = arg_group.get_unique_clause("id")?;
        if let Some(id) = &id {
            if id.contains(':') {
                return Err(ArgumentError::new(format!("A toggle's id cannot contain any colons. Offending id: {}", id)));
            }
        }
        
        Ok(ToggleArg {
            input_key, output_keys, mode, id
        })
    }

    // The size of the ToggleState we need to reserve for this toggle.
    pub fn size(&self) -> usize {
        self.output_keys.len()
    }
}0707010000002E000081A4000000000000000000000001647DB2300000110D000000000000000000000000000000000000002A00000000evsieve-1.4.0~0/src/arguments/withhold.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::event::EventType;
use crate::error::ArgumentError;
use crate::arguments::lib::ComplexArgGroup;
use crate::arguments::hook::HookArg;
use crate::stream::hook::Trigger;
use crate::key::{Key, KeyParser};

// The --withhold argument imposes the following rules upon the preceding hooks:
// 1. None of the hooks may dispatch events (via send-key) that can match any of the preceding hooks.
// 2. Only events of type EV_KEY (key/btn) can be withheld.
// 3. All keys on the hook/withhold that may interact with withheld events must be pure.

/// Represents a --withhold argument.
pub(super) struct WithholdArg {
    pub keys: Vec<Key>,
    /// All the triggers of all --hook arguments that come before a --withhold argument.
    pub associated_triggers: Vec<Trigger>,
}

impl WithholdArg {
	pub fn parse(args: Vec<String>) -> Result<WithholdArg, ArgumentError> {
        let arg_group = ComplexArgGroup::parse(args,
            &[],
            &[],
            false,
            true,
        )?;

        let mut parser = KeyParser::pure();
        parser.forbid_non_EV_KEY = true;
        let keys = parser.parse_all(&arg_group.get_keys_or_empty_key())?;

        Ok(WithholdArg { keys, associated_triggers: Vec::new() })
    }

    pub fn associate_hooks(&mut self, hooks: &mut [&mut HookArg]) -> Result<(), ArgumentError> {
        if hooks.is_empty() {
            return Err(ArgumentError::new("A --withhold argument must be preceded by at least one --hook argument."));
        }

        // Determine all keys that can be send from --hook send-key.
        let sendable_keys: Vec<&Key> = hooks.iter().flat_map(|hook| &hook.send_keys).collect();

        // Verify that the constraints on the preceding hooks are upheld.
        for hook_arg in hooks.iter() {
            for (key, key_str) in &hook_arg.keys_and_str {
                // Make sure no hook can match on a key that can be sent from the same set.
                if sendable_keys.iter().any(|send_key| send_key.intersects_with(key)) {
                    return Err(ArgumentError::new(format!(
                        "It is not possible to use --withhold on a set of hooks where any of the hooks has an input key that matches any event that can be dispatched from any of the send-key= clauses. The key \"{}\" violates this constraint.", key_str
                    )));
                }

                // If no events that match this trigger will ever be withheld, we do not need
                // to impose further restrictions on this trigger.
                if ! self.keys.iter().any(|self_key| self_key.intersects_with(key)) {
                    continue;
                }

                // Make sure that all triggers whose associated may possibly be withheld can
                // only trigger on events of type EV_KEY.
                match key.requires_event_type() {
                    Some(EventType::KEY) => (),
                    None => return Err(ArgumentError::new(format!(
                        "Cannot use --withhold after a hook that triggers on the key \"{}\", because this key can be triggered by events of any event type.",
                        key_str,
                    ))),
                    Some(_) => return Err(ArgumentError::new(format!(
                        "Cannot use --withhold after a hook that triggers on the key \"{}\". Only events of type \"key\" or \"btn\" can be withheld. If you wish for this --withhold to ignore non-EV_KEY-type events, then you can get rid of this error by explicitly specifying \"--withhold key btn\".",
                        key_str,
                    ))),
                }

                // Only permit matching with default (unspecified) values.
                let pedantic_parser = KeyParser::pure().and_filter(super::hook::PARSER);
                if pedantic_parser.parse(key_str).is_err() {
                    return Err(ArgumentError::new(format!(
                        "Cannot use --withhold after a --hook that activates on events with a specific value such as \"{}\".",
                        key_str
                    )));
                }
            }
        }

        self.associated_triggers.extend(
            hooks.iter_mut()
                 .map(|hook_arg| hook_arg.compile_trigger())
        );

        Ok(())
    }
}
0707010000002F000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001D00000000evsieve-1.4.0~0/src/bindings07070100000030000081A4000000000000000000000001647DB23000000045000000000000000000000000000000000000002800000000evsieve-1.4.0~0/src/bindings/libevdev.h#include "libevdev/libevdev.h"
#include "libevdev/libevdev-uinput.h"
07070100000031000081A4000000000000000000000001647DB23000005E13000000000000000000000000000000000000002900000000evsieve-1.4.0~0/src/bindings/libevdev.rs// SPDX-License-Identifier: MIT
// 
// 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./* automatically generated by rust-bindgen 0.59.2 */

pub const EV_SYN: u32 = 0;
pub const EV_KEY: u32 = 1;
pub const EV_REL: u32 = 2;
pub const EV_ABS: u32 = 3;
pub const EV_MSC: u32 = 4;
pub const EV_SW: u32 = 5;
pub const EV_LED: u32 = 17;
pub const EV_SND: u32 = 18;
pub const EV_REP: u32 = 20;
pub const EV_FF: u32 = 21;
pub const EV_PWR: u32 = 22;
pub const EV_FF_STATUS: u32 = 23;
pub const EV_MAX: u32 = 31;
pub const EV_CNT: u32 = 32;
pub const MSC_SERIAL: u32 = 0;
pub const MSC_PULSELED: u32 = 1;
pub const MSC_GESTURE: u32 = 2;
pub const MSC_RAW: u32 = 3;
pub const MSC_SCAN: u32 = 4;
pub const MSC_TIMESTAMP: u32 = 5;
pub const MSC_MAX: u32 = 7;
pub const MSC_CNT: u32 = 8;
pub const REP_DELAY: u32 = 0;
pub const REP_PERIOD: u32 = 1;
pub const REP_MAX: u32 = 1;
pub const REP_CNT: u32 = 2;
pub const EV_VERSION: u32 = 65537;
pub type __time_t = ::std::os::raw::c_long;
pub type __suseconds_t = ::std::os::raw::c_long;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct timeval {
    pub tv_sec: __time_t,
    pub tv_usec: __suseconds_t,
}
#[test]
fn bindgen_test_layout_timeval() {
    assert_eq!(
        ::std::mem::size_of::<timeval>(),
        16usize,
        concat!("Size of: ", stringify!(timeval))
    );
    assert_eq!(
        ::std::mem::align_of::<timeval>(),
        8usize,
        concat!("Alignment of ", stringify!(timeval))
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<timeval>())).tv_sec as *const _ as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(timeval),
            "::",
            stringify!(tv_sec)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<timeval>())).tv_usec as *const _ as usize },
        8usize,
        concat!(
            "Offset of field: ",
            stringify!(timeval),
            "::",
            stringify!(tv_usec)
        )
    );
}
pub type size_t = ::std::os::raw::c_ulong;
pub type __u16 = ::std::os::raw::c_ushort;
pub type __s32 = ::std::os::raw::c_int;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct input_event {
    pub time: timeval,
    pub type_: __u16,
    pub code: __u16,
    pub value: __s32,
}
#[test]
fn bindgen_test_layout_input_event() {
    assert_eq!(
        ::std::mem::size_of::<input_event>(),
        24usize,
        concat!("Size of: ", stringify!(input_event))
    );
    assert_eq!(
        ::std::mem::align_of::<input_event>(),
        8usize,
        concat!("Alignment of ", stringify!(input_event))
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_event>())).time as *const _ as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(input_event),
            "::",
            stringify!(time)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_event>())).type_ as *const _ as usize },
        16usize,
        concat!(
            "Offset of field: ",
            stringify!(input_event),
            "::",
            stringify!(type_)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_event>())).code as *const _ as usize },
        18usize,
        concat!(
            "Offset of field: ",
            stringify!(input_event),
            "::",
            stringify!(code)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_event>())).value as *const _ as usize },
        20usize,
        concat!(
            "Offset of field: ",
            stringify!(input_event),
            "::",
            stringify!(value)
        )
    );
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct input_absinfo {
    pub value: __s32,
    pub minimum: __s32,
    pub maximum: __s32,
    pub fuzz: __s32,
    pub flat: __s32,
    pub resolution: __s32,
}
#[test]
fn bindgen_test_layout_input_absinfo() {
    assert_eq!(
        ::std::mem::size_of::<input_absinfo>(),
        24usize,
        concat!("Size of: ", stringify!(input_absinfo))
    );
    assert_eq!(
        ::std::mem::align_of::<input_absinfo>(),
        4usize,
        concat!("Alignment of ", stringify!(input_absinfo))
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_absinfo>())).value as *const _ as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(input_absinfo),
            "::",
            stringify!(value)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_absinfo>())).minimum as *const _ as usize },
        4usize,
        concat!(
            "Offset of field: ",
            stringify!(input_absinfo),
            "::",
            stringify!(minimum)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_absinfo>())).maximum as *const _ as usize },
        8usize,
        concat!(
            "Offset of field: ",
            stringify!(input_absinfo),
            "::",
            stringify!(maximum)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_absinfo>())).fuzz as *const _ as usize },
        12usize,
        concat!(
            "Offset of field: ",
            stringify!(input_absinfo),
            "::",
            stringify!(fuzz)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_absinfo>())).flat as *const _ as usize },
        16usize,
        concat!(
            "Offset of field: ",
            stringify!(input_absinfo),
            "::",
            stringify!(flat)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<input_absinfo>())).resolution as *const _ as usize },
        20usize,
        concat!(
            "Offset of field: ",
            stringify!(input_absinfo),
            "::",
            stringify!(resolution)
        )
    );
}
pub type va_list = __builtin_va_list;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct libevdev {
    _unused: [u8; 0],
}
pub const libevdev_read_flag_LIBEVDEV_READ_FLAG_SYNC: libevdev_read_flag = 1;
pub const libevdev_read_flag_LIBEVDEV_READ_FLAG_NORMAL: libevdev_read_flag = 2;
pub const libevdev_read_flag_LIBEVDEV_READ_FLAG_FORCE_SYNC: libevdev_read_flag = 4;
pub const libevdev_read_flag_LIBEVDEV_READ_FLAG_BLOCKING: libevdev_read_flag = 8;
pub type libevdev_read_flag = ::std::os::raw::c_uint;
extern "C" {
    pub fn libevdev_new() -> *mut libevdev;
}
extern "C" {
    pub fn libevdev_new_from_fd(
        fd: ::std::os::raw::c_int,
        dev: *mut *mut libevdev,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_free(dev: *mut libevdev);
}
pub const libevdev_log_priority_LIBEVDEV_LOG_ERROR: libevdev_log_priority = 10;
pub const libevdev_log_priority_LIBEVDEV_LOG_INFO: libevdev_log_priority = 20;
pub const libevdev_log_priority_LIBEVDEV_LOG_DEBUG: libevdev_log_priority = 30;
pub type libevdev_log_priority = ::std::os::raw::c_uint;
pub type libevdev_log_func_t = ::std::option::Option<
    unsafe extern "C" fn(
        priority: libevdev_log_priority,
        data: *mut ::std::os::raw::c_void,
        file: *const ::std::os::raw::c_char,
        line: ::std::os::raw::c_int,
        func: *const ::std::os::raw::c_char,
        format: *const ::std::os::raw::c_char,
        args: *mut __va_list_tag,
    ),
>;
extern "C" {
    pub fn libevdev_set_log_function(
        logfunc: libevdev_log_func_t,
        data: *mut ::std::os::raw::c_void,
    );
}
extern "C" {
    pub fn libevdev_set_log_priority(priority: libevdev_log_priority);
}
extern "C" {
    pub fn libevdev_get_log_priority() -> libevdev_log_priority;
}
pub type libevdev_device_log_func_t = ::std::option::Option<
    unsafe extern "C" fn(
        dev: *const libevdev,
        priority: libevdev_log_priority,
        data: *mut ::std::os::raw::c_void,
        file: *const ::std::os::raw::c_char,
        line: ::std::os::raw::c_int,
        func: *const ::std::os::raw::c_char,
        format: *const ::std::os::raw::c_char,
        args: *mut __va_list_tag,
    ),
>;
extern "C" {
    pub fn libevdev_set_device_log_function(
        dev: *mut libevdev,
        logfunc: libevdev_device_log_func_t,
        priority: libevdev_log_priority,
        data: *mut ::std::os::raw::c_void,
    );
}
pub const libevdev_grab_mode_LIBEVDEV_GRAB: libevdev_grab_mode = 3;
pub const libevdev_grab_mode_LIBEVDEV_UNGRAB: libevdev_grab_mode = 4;
pub type libevdev_grab_mode = ::std::os::raw::c_uint;
extern "C" {
    pub fn libevdev_grab(dev: *mut libevdev, grab: libevdev_grab_mode) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_fd(dev: *mut libevdev, fd: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_change_fd(
        dev: *mut libevdev,
        fd: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_fd(dev: *const libevdev) -> ::std::os::raw::c_int;
}
pub const libevdev_read_status_LIBEVDEV_READ_STATUS_SUCCESS: libevdev_read_status = 0;
pub const libevdev_read_status_LIBEVDEV_READ_STATUS_SYNC: libevdev_read_status = 1;
pub type libevdev_read_status = ::std::os::raw::c_uint;
extern "C" {
    pub fn libevdev_next_event(
        dev: *mut libevdev,
        flags: ::std::os::raw::c_uint,
        ev: *mut input_event,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_has_event_pending(dev: *mut libevdev) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_name(dev: *const libevdev) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_set_name(dev: *mut libevdev, name: *const ::std::os::raw::c_char);
}
extern "C" {
    pub fn libevdev_get_phys(dev: *const libevdev) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_set_phys(dev: *mut libevdev, phys: *const ::std::os::raw::c_char);
}
extern "C" {
    pub fn libevdev_get_uniq(dev: *const libevdev) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_set_uniq(dev: *mut libevdev, uniq: *const ::std::os::raw::c_char);
}
extern "C" {
    pub fn libevdev_get_id_product(dev: *const libevdev) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_id_product(dev: *mut libevdev, product_id: ::std::os::raw::c_int);
}
extern "C" {
    pub fn libevdev_get_id_vendor(dev: *const libevdev) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_id_vendor(dev: *mut libevdev, vendor_id: ::std::os::raw::c_int);
}
extern "C" {
    pub fn libevdev_get_id_bustype(dev: *const libevdev) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_id_bustype(dev: *mut libevdev, bustype: ::std::os::raw::c_int);
}
extern "C" {
    pub fn libevdev_get_id_version(dev: *const libevdev) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_id_version(dev: *mut libevdev, version: ::std::os::raw::c_int);
}
extern "C" {
    pub fn libevdev_get_driver_version(dev: *const libevdev) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_has_property(
        dev: *const libevdev,
        prop: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_enable_property(
        dev: *mut libevdev,
        prop: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_disable_property(
        dev: *mut libevdev,
        prop: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_has_event_type(
        dev: *const libevdev,
        type_: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_has_event_code(
        dev: *const libevdev,
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_abs_minimum(
        dev: *const libevdev,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_abs_maximum(
        dev: *const libevdev,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_abs_fuzz(
        dev: *const libevdev,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_abs_flat(
        dev: *const libevdev,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_abs_resolution(
        dev: *const libevdev,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_abs_info(
        dev: *const libevdev,
        code: ::std::os::raw::c_uint,
    ) -> *const input_absinfo;
}
extern "C" {
    pub fn libevdev_get_event_value(
        dev: *const libevdev,
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_event_value(
        dev: *mut libevdev,
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        value: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_fetch_event_value(
        dev: *const libevdev,
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        value: *mut ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_slot_value(
        dev: *const libevdev,
        slot: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_slot_value(
        dev: *mut libevdev,
        slot: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        value: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_fetch_slot_value(
        dev: *const libevdev,
        slot: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        value: *mut ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_num_slots(dev: *const libevdev) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_current_slot(dev: *const libevdev) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_abs_minimum(
        dev: *mut libevdev,
        code: ::std::os::raw::c_uint,
        val: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn libevdev_set_abs_maximum(
        dev: *mut libevdev,
        code: ::std::os::raw::c_uint,
        val: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn libevdev_set_abs_fuzz(
        dev: *mut libevdev,
        code: ::std::os::raw::c_uint,
        val: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn libevdev_set_abs_flat(
        dev: *mut libevdev,
        code: ::std::os::raw::c_uint,
        val: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn libevdev_set_abs_resolution(
        dev: *mut libevdev,
        code: ::std::os::raw::c_uint,
        val: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn libevdev_set_abs_info(
        dev: *mut libevdev,
        code: ::std::os::raw::c_uint,
        abs: *const input_absinfo,
    );
}
extern "C" {
    pub fn libevdev_enable_event_type(
        dev: *mut libevdev,
        type_: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_disable_event_type(
        dev: *mut libevdev,
        type_: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_enable_event_code(
        dev: *mut libevdev,
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        data: *const ::std::os::raw::c_void,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_disable_event_code(
        dev: *mut libevdev,
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_kernel_set_abs_info(
        dev: *mut libevdev,
        code: ::std::os::raw::c_uint,
        abs: *const input_absinfo,
    ) -> ::std::os::raw::c_int;
}
pub const libevdev_led_value_LIBEVDEV_LED_ON: libevdev_led_value = 3;
pub const libevdev_led_value_LIBEVDEV_LED_OFF: libevdev_led_value = 4;
pub type libevdev_led_value = ::std::os::raw::c_uint;
extern "C" {
    pub fn libevdev_kernel_set_led_value(
        dev: *mut libevdev,
        code: ::std::os::raw::c_uint,
        value: libevdev_led_value,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_kernel_set_led_values(dev: *mut libevdev, ...) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_set_clock_id(
        dev: *mut libevdev,
        clockid: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_is_type(
        ev: *const input_event,
        type_: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_is_code(
        ev: *const input_event,
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_type_get_name(
        type_: ::std::os::raw::c_uint,
    ) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_event_code_get_name(
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
    ) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_event_value_get_name(
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        value: ::std::os::raw::c_int,
    ) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_property_get_name(
        prop: ::std::os::raw::c_uint,
    ) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_event_type_get_max(type_: ::std::os::raw::c_uint) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_type_from_name(
        name: *const ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_type_from_name_n(
        name: *const ::std::os::raw::c_char,
        len: size_t,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_code_from_name(
        type_: ::std::os::raw::c_uint,
        name: *const ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_code_from_name_n(
        type_: ::std::os::raw::c_uint,
        name: *const ::std::os::raw::c_char,
        len: size_t,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_value_from_name(
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        name: *const ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_type_from_code_name(
        name: *const ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_type_from_code_name_n(
        name: *const ::std::os::raw::c_char,
        len: size_t,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_code_from_code_name(
        name: *const ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_code_from_code_name_n(
        name: *const ::std::os::raw::c_char,
        len: size_t,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_event_value_from_name_n(
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        name: *const ::std::os::raw::c_char,
        len: size_t,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_property_from_name(
        name: *const ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_property_from_name_n(
        name: *const ::std::os::raw::c_char,
        len: size_t,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_get_repeat(
        dev: *const libevdev,
        delay: *mut ::std::os::raw::c_int,
        period: *mut ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct libevdev_uinput {
    _unused: [u8; 0],
}
pub const libevdev_uinput_open_mode_LIBEVDEV_UINPUT_OPEN_MANAGED: libevdev_uinput_open_mode = -2;
pub type libevdev_uinput_open_mode = ::std::os::raw::c_int;
extern "C" {
    pub fn libevdev_uinput_create_from_device(
        dev: *const libevdev,
        uinput_fd: ::std::os::raw::c_int,
        uinput_dev: *mut *mut libevdev_uinput,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_uinput_destroy(uinput_dev: *mut libevdev_uinput);
}
extern "C" {
    pub fn libevdev_uinput_get_fd(uinput_dev: *const libevdev_uinput) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn libevdev_uinput_get_syspath(
        uinput_dev: *mut libevdev_uinput,
    ) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_uinput_get_devnode(
        uinput_dev: *mut libevdev_uinput,
    ) -> *const ::std::os::raw::c_char;
}
extern "C" {
    pub fn libevdev_uinput_write_event(
        uinput_dev: *const libevdev_uinput,
        type_: ::std::os::raw::c_uint,
        code: ::std::os::raw::c_uint,
        value: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
pub type __builtin_va_list = [__va_list_tag; 1usize];
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct __va_list_tag {
    pub gp_offset: ::std::os::raw::c_uint,
    pub fp_offset: ::std::os::raw::c_uint,
    pub overflow_arg_area: *mut ::std::os::raw::c_void,
    pub reg_save_area: *mut ::std::os::raw::c_void,
}
#[test]
fn bindgen_test_layout___va_list_tag() {
    assert_eq!(
        ::std::mem::size_of::<__va_list_tag>(),
        24usize,
        concat!("Size of: ", stringify!(__va_list_tag))
    );
    assert_eq!(
        ::std::mem::align_of::<__va_list_tag>(),
        8usize,
        concat!("Alignment of ", stringify!(__va_list_tag))
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<__va_list_tag>())).gp_offset as *const _ as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(__va_list_tag),
            "::",
            stringify!(gp_offset)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<__va_list_tag>())).fp_offset as *const _ as usize },
        4usize,
        concat!(
            "Offset of field: ",
            stringify!(__va_list_tag),
            "::",
            stringify!(fp_offset)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<__va_list_tag>())).overflow_arg_area as *const _ as usize },
        8usize,
        concat!(
            "Offset of field: ",
            stringify!(__va_list_tag),
            "::",
            stringify!(overflow_arg_area)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<__va_list_tag>())).reg_save_area as *const _ as usize },
        16usize,
        concat!(
            "Offset of field: ",
            stringify!(__va_list_tag),
            "::",
            stringify!(reg_save_area)
        )
    );
}
07070100000032000081A4000000000000000000000001647DB230000031E6000000000000000000000000000000000000002200000000evsieve-1.4.0~0/src/capability.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::collections::{HashMap, HashSet};
use crate::event::{EventType, EventCode, EventValue, Namespace};
use crate::domain::Domain;
use crate::range::Range;
use crate::ecodes;
use crate::bindings::libevdev;

const EV_REP_CODES: &[EventCode] = &[
    EventCode::new(EventType::REP, ecodes::REP_DELAY),
    EventCode::new(EventType::REP, ecodes::REP_PERIOD),
];

/// Represents a map that maps an input domain to a list of capabilities which that domain is expected
/// to be able to produce now or in the future.
///
/// The InputCapabilites are often kept track of separately of the InputDevices because the
/// InputCapabilities may also include tentative capabilities of devices that are currently not available.
pub type InputCapabilites = HashMap<Domain, Capabilities>;

/// When we want to know whether a certain capability will trigger a map, we might not
/// be sure because it depends on detailed event or runtime information. In this case,
/// some test may return "Maybe" and we need to hedge our bets against both matching and
/// not matching.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum CapMatch {
    Yes,
    No,
    Maybe,
}
use CapMatch::{Yes, No, Maybe};

impl PartialOrd for CapMatch {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(Ord::cmp(self, other))
    }
}

impl Ord for CapMatch {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        let self_val  = match self  {Yes => 2, Maybe => 1, No => 0};
        let other_val = match other {Yes => 2, Maybe => 1, No => 0};
        self_val.cmp(&other_val)
    }
}

impl From<bool> for CapMatch {
    fn from(src: bool) -> Self {
        match src {
            true => CapMatch::Yes,
            false => CapMatch::No,
        }
    }
}

#[derive(Clone, PartialEq, Eq)]
pub struct Capabilities {
    /// All pairs of (type, code) supported by a device.
    pub codes: HashSet<EventCode>,
    /// Additional information for the EV_ABS event types.
    pub abs_info: HashMap<EventCode, AbsInfo>,
    /// Additional information about the repeat events that happen on EV_KEY, associated with EV_REP.
    pub rep_info: Option<RepeatInfo>,
}

/// Represents the value related to EV_REP.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct RepeatInfo {
    /// REP_DELAY
    pub delay: EventValue,
    /// REP_PERIOD
    pub period: EventValue,
}

impl RepeatInfo {
    /// The kernel is ultimately going to ignore our choice of repeat info anyway, but we like
    /// to keep track of the real values in case the kernel gets fixed sometime. In case we don't
    /// have access to real values, this tells us what the kernel defaults are.
    pub fn kernel_default() -> RepeatInfo {
        RepeatInfo {
            delay: 250,
            period: 33,
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct AbsInfo {
    pub min_value: EventValue,
    pub max_value: EventValue,
    pub meta: AbsMeta,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct AbsMeta {
    pub fuzz: i32,
    pub flat: i32,
    pub resolution: i32,
    pub value: i32,
}

impl From<AbsInfo> for libevdev::input_absinfo {
    fn from(abs_info: AbsInfo) -> libevdev::input_absinfo {
        libevdev::input_absinfo {
            flat: abs_info.meta.flat,
            fuzz: abs_info.meta.fuzz,
            resolution: abs_info.meta.resolution,
            minimum: std::cmp::min(abs_info.min_value, abs_info.max_value),
            maximum: std::cmp::max(abs_info.min_value, abs_info.max_value),
            value: abs_info.meta.value,
        }
    }
}

impl From<libevdev::input_absinfo> for AbsInfo {
    fn from(abs_info: libevdev::input_absinfo) -> AbsInfo {
        AbsInfo {
            min_value: abs_info.minimum,
            max_value: abs_info.maximum,
            meta: AbsMeta {
                flat: abs_info.flat,
                fuzz: abs_info.fuzz,
                resolution: abs_info.resolution,
                value: abs_info.value,
            }
        }
    }
}

impl Capabilities {
    pub fn new() -> Capabilities {
        Capabilities {
            codes: HashSet::new(),
            abs_info: HashMap::new(),
            rep_info: None,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.codes.is_empty()
    }

    /// Returns true if this Capabilities is not capable of any non-trivial event codes, where
    /// events such as EV_SYN or EV_REP are deemed trivial.
    pub fn has_no_content(&self) -> bool {
        let trivial_types = [EventType::SYN, EventType::REP];
        return self.codes.iter().all(|code| trivial_types.contains(&code.ev_type()));
    }

    pub fn to_vec_from_domain_and_namespace(&self, domain: Domain, namespace: Namespace) -> Vec<Capability> {
        self.codes.iter().filter_map(|&code| {
            let abs_info = self.abs_info.get(&code);
            let (value_range, abs_meta) = match abs_info {
                None => match code.ev_type() {
                    EventType::KEY => (Range::new(Some(0), Some(2)), None),
                    _ => (Range::new(None, None), None),
                },
                Some(info) => (
                    Range::new(Some(info.min_value), Some(info.max_value)),
                    Some(info.meta),
                ),
            };

            // The EV_REP capability signifies that the kernel has been asked to automatically
            // generate repeat events, not the ability to generate repeat events. As such, it
            // doesn't make sense to propagate EV_REP capabilities.
            if code.ev_type().is_rep() {
                None
            } else {
                Some(Capability { code, domain, value_range, abs_meta, namespace })
            }
        }).collect()
    }

    pub fn add_capability(&mut self, cap: Capability) {
        self.codes.insert(cap.code);

        // For events of type EV_ABS, an accompanying AbsInfo is required.
        if cap.code.ev_type().is_abs() {
            // It is possible to lack abs_meta on this capability, e.g. if some non-abs event got
            // mapped to an abs-event. In that case, use the sanest default we can think of.
            let meta = match cap.abs_meta {
                Some(meta) => meta,
                None => AbsMeta { flat: 0, fuzz: 0, resolution: 0, value: 0 },
            };

            // Check if we already know something about this axis from another source. If so, we
            // should merge this capability with that one. Otherwise, for code simplicity we assume
            // that the current info is the same as that of this new capability.
            let existing_info = self.abs_info.get(&cap.code);
            let (current_range, current_meta) = match existing_info {
                Some(info) => (Range::new(Some(info.min_value), Some(info.max_value)), info.meta),
                None => (cap.value_range, meta),
            };

            // Merge the current info with this capability.
            let new_range = current_range.merge(&cap.value_range);
            let new_meta = AbsMeta {
                // Merging is hard. I don't know whether min or max is most appropriate for these.
                flat: std::cmp::min(current_meta.flat, meta.flat),
                fuzz: std::cmp::min(current_meta.fuzz, meta.fuzz),
                resolution: std::cmp::max(current_meta.resolution, meta.resolution),
                value: new_range.bound(meta.value),
            };

            // We might get an unbounded range in case we mapped some non-abs non-key event with
            // an unknown value range.
            if ! new_range.is_bounded() {
                eprintln!("Warning: could not automatically derive the possible range of the absolute axis {}.", ecodes::event_name(cap.code));
            };
            let min_value = new_range.min.discrete_or(i32::MIN);
            let max_value = new_range.max.discrete_or(i32::MAX);

            // Insert or overwrite the existing value.
            self.abs_info.insert(cap.code, AbsInfo {
                min_value, max_value, meta: new_meta
            });
        }
    }

    /// Adds EV_REP capabilities to self with arbitrary delay and period.
    /// The kernel is going to ignore the delay and period we give it anyway.
    pub fn require_ev_rep(&mut self) {
        if self.rep_info.is_none() {
            self.set_ev_rep(RepeatInfo::kernel_default())
        }
    }

    /// Removes EV_REP cababilities from self.
    pub fn remove_ev_rep(&mut self) {
        self.rep_info = None;
        for code in EV_REP_CODES {
            self.codes.remove(code);
        }
    }

    /// Sets the rep_info variable of self and makes sure that the correct capabilities
    /// are inserted to self.codes.
    fn set_ev_rep(&mut self, repeat_info: RepeatInfo) {
        self.rep_info = Some(repeat_info);
        for &code in EV_REP_CODES {
            self.codes.insert(code);
        }
    }

    pub fn ev_types(&self) -> HashSet<EventType> {
        self.codes.iter()
            .map(|code| code.ev_type())
            .collect()
    }

    /// Given a device that has output capabilities `other`, can we properly write all events corrosponding
    /// to the capabilities of `self` to that device? Returns true if we can, false if there may be issues.
    ///
    /// To be true, `other` must have all event codes of `self` and identical absolute axes. Ignores the
    /// current value of absolute axes.
    pub fn is_compatible_with(&self, other: &Capabilities) -> bool {
        if ! self.codes.is_subset(&other.codes) {
            return false;
        }
        for (code, info) in &self.abs_info {
            if let Some(other_info) = other.abs_info.get(code) {
                // Avoid getting incompatibility due to a different meta.value, but do compare all
                // other properties of the absolute axes.
                let mut other_info: AbsInfo = *other_info;
                other_info.meta.value = info.meta.value;

                if *info != other_info {
                    return false;
                }
            } else {
                return false;
            }
        }
        // We don't care about self.rep_info because the kernel doesn't either.

        true
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Capability {
    pub code: EventCode,
    pub domain: Domain,
    pub namespace: Namespace,
    pub value_range: Range,
    pub abs_meta: Option<AbsMeta>,
}

impl Capability {
    /// Returns a copy of self with an universal value range, and the original range.
    fn split_value(mut self) -> (Capability, Range) {
        let value = self.value_range;
        self.value_range = Range::new(None, None);
        (self, value)
    }

    /// Returns a copy of self with the given value range.
    fn with_value(mut self, range: Range) -> Capability {
        self.value_range = range;
        self
    }
}

/// Tries to simplify a vec of capabilites by merging similar capabilities (those that differ
/// only in value) together. This avoids a worst-case scenario of exponential complexity for some
/// degenerate input arguments.
pub fn aggregate_capabilities(capabilities: Vec<Capability>) -> Vec<Capability> {
    // Sort the capabilities into those which only differ by value.
    let mut values_by_capability: HashMap<Capability, Vec<Range>> = HashMap::new();
    for capability in capabilities {
        let (key, value) = capability.split_value();
        values_by_capability.entry(key).or_insert_with(Vec::new).push(value);
    }

    // Try to merge the values.
    let mut results: Vec<Capability> = Vec::new();
    for (capability, mut values) in values_by_capability {
        values.sort_by_key(|range| range.min);
        let mut values_iter = values.into_iter();
        let mut merged_values: Vec<Range> = match values_iter.next() {
            Some(value) => vec![value],
            None => continue,
        };
        for value in values_iter {
            let last_value = merged_values.last_mut().unwrap();
            match value.try_union(last_value) {
                Some(union_value) => *last_value = union_value,
                None => merged_values.push(value),
            }
        }
        for value in merged_values {
            results.push(capability.with_value(value));
        }
    }

    results
}

/// Given an InputCapabilites, generates a vector that contains every discrete capability that can be
/// generated by the corresponding input devices.
pub fn input_caps_to_vec(caps: &InputCapabilites) -> Vec<Capability> {
    caps.iter()
        .flat_map(|(domain, caps)| caps.to_vec_from_domain_and_namespace(*domain, Namespace::Input))
        .collect()
}07070100000033000081A4000000000000000000000001647DB23000000B3B000000000000000000000000000000000000002400000000evsieve-1.4.0~0/src/control_fifo.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::os::unix::io::{RawFd, AsRawFd};

use crate::error::{SystemError, ArgumentError, Context};
use crate::io::fd::HasFixedFd;
use crate::io::fifo::Fifo;
use crate::arguments::hook::HookToggleAction;
use crate::stream::Setup;
use crate::io::fifo::LineRead;

pub struct ControlFifo {
    source: Box<dyn LineRead>,
    path: String,
}

impl ControlFifo {
    // TODO: FEATURE(control-fifo) Reuse existing Fifo's on the filesystem.
    pub fn create(path: String) -> Result<ControlFifo, SystemError> {
        let source = Box::new(Fifo::create(&path)?);
        Ok(ControlFifo { path, source })
    }

    /// IMPORTANT: this function should never return ArgumentError, because then the fifo would
    /// get closed in case the user provides an incorrect command. Only return SystemError to
    /// signal that something is wrong with the underlying file.
    pub fn poll(&mut self) -> Result<Vec<Command>, SystemError> {
        let lines = self.source.read_lines()?;
        let commands = lines.into_iter()
            .filter(|line| !line.is_empty())
            .filter_map(|line| match parse_command(&line) {
                Ok(effect) => Some(effect),
                Err(error) => {
                    error.with_context(format!("While parsing the command {}:", line)).print_err();
                    None
                }
            }
            ).collect();
        Ok(commands)
    }

    pub fn path(&self) -> &str {
        &self.path
    }
}

pub enum Command {
    Toggle(HookToggleAction),
}

fn parse_command(line: &str) -> Result<Command, ArgumentError> {
    let mut parts = line.split_whitespace();
    let command = match parts.next() {
        Some(command) => command,
        None => return Err(ArgumentError::new("No command provided.")),
    };
    let args: Vec<&str> = parts.collect();

    match command {
        "toggle" => {
            let has_toggle_flag = args.is_empty();
            let toggle_clauses = args.into_iter().map(str::to_owned).collect();
            Ok(Command::Toggle(
                HookToggleAction::parse(has_toggle_flag, toggle_clauses)?
            ))
        },
        _ => Err(ArgumentError::new(format!("Unknown command name: {}", command))),
    }
}

impl Command {
    pub fn execute(self, setup: &mut Setup) -> Result<(), ArgumentError> {
        match self {
            Command::Toggle(action) => {
                // TODO: FEATURE(control-fifo) More helpful error.
                let effects = action.implement(setup.state(), setup.toggle_indices())?;
                for effect in effects {
                    effect(setup.state_mut());
                }
            }
        }

        Ok(())
    }
}

impl AsRawFd for ControlFifo {
    fn as_raw_fd(&self) -> RawFd {
        self.source.as_raw_fd()
    }
}
unsafe impl HasFixedFd for ControlFifo {}07070100000034000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001B00000000evsieve-1.4.0~0/src/daemon07070100000035000081A4000000000000000000000001647DB2300000010E000000000000000000000000000000000000001E00000000evsieve-1.4.0~0/src/daemon.rs// SPDX-License-Identifier: GPL-2.0-or-later

mod systemd;

pub fn notify_ready_async() {
    if systemd::is_available() {
        systemd::notify_ready();
    }
}

pub fn await_completion() {
    if systemd::is_available() {
        systemd::await_completion();
    }
}07070100000036000081A4000000000000000000000001647DB230000010BE000000000000000000000000000000000000002600000000evsieve-1.4.0~0/src/daemon/systemd.rs// SPDX-License-Identifier: GPL-2.0-or-later

#![allow(unused_imports)]
use std::os::raw::{c_int, c_char};
use std::process::{Command, Stdio};
use std::io::ErrorKind;
use std::sync::{Mutex, Barrier, Arc};

/// The systemd feature links against libsystemd instead of falling back on the slower systemd-notify
/// tool. It is currently unused because it complicates the build process.
#[cfg(feature = "systemd")]
extern "C" {
    fn sd_notify(unset_environment: c_int, state: *const c_char) -> c_int;
}

#[cfg(feature = "systemd")]
fn notify(state: &str) {
    let state_cstring = std::ffi::CString::new(state).unwrap();
    let _result = unsafe { sd_notify(0, state_cstring.as_ptr()) };
}

lazy_static! {
    /// This Mutex will be locked as long as an asynchronous attempt to notify systemd that we're ready
    /// is in progress, and is released when that attempt completes. As such, trying to lock this Mutex
    /// and then releasing that lock is a way to avoid interrupting notification in progress.
    static ref DAEMON_NOTIFICATION_IN_PROGRESS: Mutex<()> = Mutex::new(());
}

#[cfg(not(feature = "systemd"))]
fn notify(state: &str) {
    // TODO: LOW-PRIORITY: consider using the subprocess subsystem for this.
    if ! is_available() {
        return;
    }

    let child_res = Command::new("systemd-notify")
        .args(["--", state])
        .stdin(Stdio::null())
        .spawn();

    match child_res {
        Err(error) => {
            match error.kind() {
                ErrorKind::NotFound => { eprintln!("Warning: the environment variable NOTIFY_SOCKET was set, suggesting that evsieve is being ran as a systemd service. However, the systemd-notify tool was not found. Evsieve is unable to notify the hypervisor that evsieve is ready."); },
                _ => { eprintln!("Warning: the environment variable NOTIFY_SOCKET was set, suggesting that evsieve is being ran as a systemd service. However, we failed to notify the systemd supervisor that evsieve is ready.\nError encountered: {}", error); },
            }
        },
        Ok(mut child) => {
            // Use a barrier to wait until the thread notifies us that the notification lock has been
            // acquired before this function returns.
            let barrier = Arc::new(Barrier::new(2));
            let barrier_clone = Arc::clone(&barrier);

            std::thread::spawn(move || {
                // We don't care if the lock returns Err because it is poisoned.
                let _lock = DAEMON_NOTIFICATION_IN_PROGRESS.lock();
                barrier_clone.wait();

                let notify_result = child.wait();
                match notify_result {
                    Ok(return_code) => {
                        if ! return_code.success() {
                            match return_code.code() {
                                Some(code) => eprintln!("Warning: the environment variable NOTIFY_SOCKET was set, suggesting that evsieve is being ran as a systemd service. However, the systemd-notify tool returned the following error code when we tried to inform the hypervisor that evsieve is ready: {}", code),
                                None => eprintln!("Warning: the environment variable NOTIFY_SOCKET was set, suggesting that evsieve is being ran as a systemd service. However, the systemd-notify tool failed to notify the hypervisor for an unknown reason."),
                            }
                        }
                    },
                    Err(error) => {
                        eprintln!("Warning: the environment variable NOTIFY_SOCKET was set, suggesting that evsieve is being ran as a systemd service. However, we failed to notify the systemd supervisor that evsieve is ready.\nError encountered: {}", error);
                    }
                }
            });

            barrier.wait();
        }
    }
}

/// Tries to notify the daemon that evsieve is ready. Depending on implementation, this notification may
/// happen asynchronously.
pub fn notify_ready() {
    notify("READY=1")
}

/// If notification is in progress, this function will wait until after it is completed.
pub fn await_completion() {
    drop(DAEMON_NOTIFICATION_IN_PROGRESS.lock());
}

pub fn is_available() -> bool {
    std::env::var("NOTIFY_SOCKET").is_ok()
}
07070100000037000081A4000000000000000000000001647DB230000022EC000000000000000000000000000000000000002A00000000evsieve-1.4.0~0/src/daemon/traditional.rs// SPDX-License-Identifier: GPL-2.0-or-later

//! A module full of dead code that can be used to add support for traditional-style daemons.
//! The module mostly works, but it adds some headache-inducing invariants (e.g. requires the
//! process to be single-threaded at some critical points). As such, I am not actually stabilizing
//! the addition of traditional daemons until I am convinced of their added value.
//!
//! Currently this module is excluded from compilation. Add it to main.rs if you intend to use it.

use std::ffi::CString;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::mem::MaybeUninit;
use std::os::unix::io::RawFd;
use std::path::{PathBuf};

use crate::error::{SystemError, Context};

pub struct TraditionalDaemon {
    _pidfile: PidFile,
    child_pipe: Option<ChildPipe>,
}

impl TraditionalDaemon {
    /// Creates a fork of the parent process and becomes mostly daemonized, but does not close the
    /// standard I/O yet. `finalize()` must later be called to close the standard I/O and inform the
    /// parent process to exit.
    ///
    /// # Safety
    /// This call closes all open file descriptors except the standard I/O. Make sure you do not have
    /// any open file descriptors you intend to write to. Also, all other threads disappear when this
    /// function is called.
    pub unsafe fn new(pidfile_path: PathBuf) -> Result<TraditionalDaemon, SystemError> {
        // Close all open file descriptors except for the standard I/O.
        let rlimit = {
            let mut rlimit_uninit: MaybeUninit<libc::rlimit> = MaybeUninit::uninit();
            let res = libc::getrlimit(libc::RLIMIT_NOFILE, rlimit_uninit.as_mut_ptr());
            match res {
                0 => rlimit_uninit.assume_init(),
                _ => return Err(SystemError::os_with_context("While polling the process' resource limits:")),
            }
        };

        // If the rlim is too big, then there is a choice: either close all of them and take an extraordinary
        // amount of CPU time, or do not do so and risk leaking file descriptors. We decided that the last
        // option is the least harmful one and set an arbitrary limit on the maximum amount of file descriptors
        // that will be closed when daemonizing.
        const MAX_RLIM_FILNO: u64 = 65536;
        for i in 3 ..= rlimit.rlim_cur.min(MAX_RLIM_FILNO) {
            libc::close(i as i32);
        }

        // Prepare pipes for communication with the child.
        // The child should send a byte (i8) equal to the pipe when it is ready. The parent shall then
        // exit with that byte as its status code.
        let mut pipe_fds: [RawFd; 2] = [-1; 2];
        if libc::pipe(&mut pipe_fds as *mut RawFd) < 0 {
            return Err(SystemError::os_with_context("While trying to create internal communication pipes:"));
        }
        let parent_pipe_fd: RawFd = pipe_fds[0];
        let child_pipe_fd: RawFd = pipe_fds[1];

        // Fork the process.
        let pid = libc::fork();
        if pid < 0 {
            // Forking failed.
            return Err(SystemError::os_with_context("While trying to fork the process:"));
        }
        if pid > 0 {
            // Forking was a success. We are the parent.
            libc::close(child_pipe_fd);
            // Wait until the child sends a byte.
            let mut buffer: [i8; 1] = [0; 1];
            loop {
                let result = libc::read(parent_pipe_fd, &mut buffer as *mut _ as *mut libc::c_void, 1);
                if result > 0 {
                    let received_byte = buffer[0];
                    libc::exit(received_byte as i32);
                }
                if result as i32 == -libc::EINTR {
                    continue;
                }
                libc::exit(libc::EXIT_FAILURE);
            }
        }
        // Else: forking was a success. We are the child.
        libc::close(parent_pipe_fd);
        let child_pipe = ChildPipe::from_raw_fd(child_pipe_fd);

        // Create a new session.
        let sid = libc::setsid();
        if sid < 0 {
            return Err(SystemError::os_with_context("While trying to acquire a session ID for the daemon:"));
        }

        // Ignore signals.
        // libc::signal(libc::SIGCHLD, libc::SIG_IGN);
        // libc::signal(libc::SIGHUP, libc::SIG_IGN);

        // Fork a second time.
        let pid = libc::fork();
        if pid < 0 {
            // Forking failed.
            return Err(SystemError::os_with_context("While trying to fork the process a second time:"));
        }
        if pid > 0 {
            // Forking was a success. We are the parent.
            libc::exit(libc::EXIT_SUCCESS);
        }

        libc::umask(0);

        // Change cwd to root directory to avoid blocking mount points from being unmounted.
        let root_dir = CString::new("/").unwrap();
        if libc::chdir(root_dir.as_ptr() as *const i8) < 0 {
            return Err(SystemError::os_with_context("While change the working directory:"));
        }

        let pidfile = PidFile::new(pidfile_path).with_context("While creating the PID file:")?;
        Ok(TraditionalDaemon {
            _pidfile: pidfile,
            child_pipe: Some(child_pipe),
        })
    }

    /// Closes the standard I/O and informs the parent process to exit. Completes the daemonization.
    ///
    /// # Safety
    ///
    /// This function can only be called from a single-threaded program. If another thread tries to
    /// write to the standard I/O while this function is running, undefined behaviour ensures.
    pub unsafe fn finalize(&mut self) {
        // Lock Rust's access to the standard I/O so no other safe Rust code dares writing to them
        // while we close and reopen their file descriptors. This does not make the code thread-safe,
        // it just makes is slightly less likely that something really bad happens if some safety
        // assumption is violated.
        let stdin = std::io::stdin();
        let _stdin_lock = stdin.lock();
        let stdout = std::io::stdout();
        let _stdout_lock = stdout.lock();
        let stderr = std::io::stderr();
        let _stderr_lock = stderr.lock();

        // Close the standard I/O and redirect them to /dev/null.
        let devnull = CString::new("/dev/null").unwrap();

        for (fileno, mode) in [(libc::STDIN_FILENO, libc::O_RDONLY),
                               (libc::STDOUT_FILENO, libc::O_WRONLY),
                               (libc::STDERR_FILENO, libc::O_WRONLY)]
        {
            if libc::close(fileno) != 0 {
                libc::exit(libc::EXIT_FAILURE);
            }
            if libc::open(devnull.as_ptr() as *const i8, mode) != fileno {
                libc::exit(libc::EXIT_FAILURE);
            }
        }

        // Inform the parent that the child process is ready.
        if let Some(mut child_pipe) = self.child_pipe.take() {
            let _ = child_pipe.send_byte(0);
        }
    }
}

impl PidFile {
    fn new(path: PathBuf) -> Result<PidFile, SystemError> {
        let mut file = OpenOptions::new()
            .write(true)
            .create_new(true)
            .open(&path)?;

        let pid: i32 = unsafe { libc::getpid() };
        write!(file, "{}", pid)?;
        file.sync_all()?;

        Ok(PidFile {
            path, _file: file
        })
    }
}

struct PidFile {
    path: PathBuf,
    _file: File,
}

impl Drop for PidFile {
    fn drop(&mut self) {
        let _ = std::fs::remove_file(&self.path);
    }
}

/// A communication channel from the forked process to the parent process.
struct ChildPipe {
    fd: Option<RawFd>,
}

impl ChildPipe {
    /// # Safety
    /// The file descriptor must be valid.
    unsafe fn from_raw_fd(fd: RawFd) -> ChildPipe {
        ChildPipe { fd: Some(fd) }
    }

    fn send_byte(&mut self, byte: i8) -> Result<(), SystemError> {
        if let Some(fd) = self.fd {
            unsafe {
                let buffer: [i8; 1] = [byte];
                loop {
                    let result = libc::write(fd, &buffer as *const _ as *const libc::c_void, std::mem::size_of::<[i8; 1]>());
                    if result > 0 {
                        libc::close(fd);
                        self.fd = None;
                        return Ok(());
                    }
                    if result < 0 {
                        return Err(SystemError::os_with_context("While communicating with parent process:"))
                    }
                }
            };
        } else {
            Err(SystemError::new("Writing to a closed pipe."))
        }
    }
}

/// In case the ChildPipe is dropped without having sent an "OK" byte yet, the child has probably
/// encountered an error. Send the byte "1" to signal that something is wrong.
impl Drop for ChildPipe {
    fn drop(&mut self) {
        if self.fd.is_some() {
            let _ = self.send_byte(1);
        }
    }
}
07070100000038000081A4000000000000000000000001647DB23000000BF0000000000000000000000000000000000000001E00000000evsieve-1.4.0~0/src/domain.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::collections::HashMap;
use std::sync::Mutex;
use crate::error::ArgumentError;

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Domain(usize);

pub fn get_unique_domain() -> Domain {
    TRACKER.lock()
        .expect("Fatal error: internal mutex poisoned.")
        .get_unique_domain()
}

/// Returns a Domain for the given name. Always returns the same string for the same name
/// in the context of a single program execution. If the provided name is not up to naming
/// standards, returns an error.
///
/// The internal domain name used must NOT start with an @.
pub fn resolve(name: &str) -> Result<Domain, ArgumentError> {
    TRACKER.lock()
        .expect("Fatal error: internal mutex poisoned.")
        .resolve(name)
}

/// Returns a String that resolves to this Domain, if it exists. Otherwise, returns None.
pub fn try_reverse_resolve(domain: Domain) -> Option<String> {
    TRACKER.lock()
        .expect("Fatal error: internal mutex poisoned.")
        .try_reverse_resolve(domain)
}

lazy_static!{
    static ref TRACKER: Mutex<DomainTracker> = Mutex::new(DomainTracker::new());
}

/// The DomainTracker is a responsible for converting strings to domains (e.g. turns the
/// "foo" part of "key:a@foo" into a usize), and also for handing out unique domains for some
/// purposes where events need a domain that is not accessible to the user.
/// 
/// Intended to be used as a singleton.
struct DomainTracker {
    name_map: HashMap<String, Domain>,
    reveres_name_map: HashMap<Domain, String>,
    /// A counter for how many domains have been allocated. Used to allocate new unique domains.
    counter: usize,
}

impl DomainTracker {
    /// Maps a string to some domain. Always returns the same domain for the same string.
    fn resolve(&mut self, name: &str) -> Result<Domain, ArgumentError> {
        if name.starts_with('@') {
            return Err(ArgumentError::new(format!("The domain \"{}\" may not start with an @.", name)));
        }
        if name == "" {
            return Err(ArgumentError::new("Domains may not be empty."));
        }

        Ok(match self.name_map.get(name) {
            Some(&domain) => domain,
            None => {
                let new_domain = self.get_unique_domain();
                self.name_map.insert(name.to_owned(), new_domain);
                self.reveres_name_map.insert(new_domain, name.to_owned());
                new_domain
            }
        })
    }

    fn try_reverse_resolve(&mut self, domain: Domain) -> Option<String> {
        self.reveres_name_map.get(&domain).cloned()
    }

    /// Returns an arbitrary domain. Said domain will never be returned again by this namespace.
    fn get_unique_domain(&mut self) -> Domain {
        let result = self.counter;
        self.counter += 1;
        Domain(result)
    }

    fn new() -> DomainTracker {
        DomainTracker {
            name_map: HashMap::new(),
            reveres_name_map: HashMap::new(),
            counter: 0,
        }
    }
}
07070100000039000081A4000000000000000000000001647DB23000002D02000000000000000000000000000000000000001E00000000evsieve-1.4.0~0/src/ecodes.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::ArgumentError;
use crate::event::{EventType, EventCode, VirtualEventType};
use crate::bindings::libevdev;
use crate::utils::{split_once, parse_cstr};
use std::borrow::Cow;
use std::collections::HashMap;
use std::convert::TryInto;

lazy_static! {
    pub static ref EVENT_TYPES: HashMap<String, EventType> = {
        let mut result = HashMap::new();
        for ev_type in 0 ..= libevdev::EV_MAX {
            if let Some(raw_name) = unsafe { parse_cstr(libevdev::libevdev_event_type_get_name(ev_type)) } {
                let (ev_prefix, name_opt) = split_once(&raw_name, "_");
                // This should not happen at the time of writing, but we make this check in case some
                // exotic new event type is added in the future.
                if ev_prefix != "EV" {
                    continue;
                }
                let name = match name_opt {
                    Some(name_uppercase) => name_uppercase.to_lowercase(),
                    None => continue,
                };
                
                result.insert(name, EventType::new(ev_type as u16));
            }
        }

        // Special disposition to make keys like btn:left able to map to EV_KEY, BTN_LEFT event
        // though EV_BTN does not exist.
        result.insert(
            "btn".to_string(),
            *result.get("key").expect("Failed to import event type data from libevdev.")
        );
        result
    };

    pub static ref EVENT_CODES: HashMap<(String, String), EventCode> = {
        let mut result = HashMap::new();
        for (ev_type_name, &ev_type) in EVENT_TYPES.iter() {
            let code_max = match event_type_get_max(ev_type) {
                Some(max) => max,
                None => continue,
            };

            for code in 0 ..= code_max {
                if let Some(raw_code_name) = unsafe { parse_cstr(libevdev::libevdev_event_code_get_name(ev_type.into(), code as u32)) } {
                    let (code_type_name, code_name_opt) = split_once(&raw_code_name, "_");

                    // This test helps us not confuse KEY_* with BTN_* events.
                    if &code_type_name.to_lowercase() != ev_type_name {
                        continue;
                    }

                    let code_name = match code_name_opt {
                        Some(name_uppercase) => name_uppercase.to_lowercase(),
                        None => continue,
                    };
                    let event_code = EventCode::new(ev_type, code);

                    result.insert((ev_type_name.to_string(), code_name), event_code);
                }
            }
        }

        result
    };

    pub static ref EVENT_NAMES: HashMap<EventCode, String> = {
        let mut result = HashMap::new();
        for ((type_name, code_name), &code) in EVENT_CODES.iter() {
            let name = format!("{}:{}", type_name, code_name);
            result.insert(code, name);
        }
        result
    };

    /// For each _named_ event code (EV_KEY, code) holds: the name of this code starts with
    /// btn: if and only if it is contained in one of the following ranges.
    ///
    /// This is precomputed for performance reasons: it provides about 30% speedup in scripts
    /// where most maps are type-level on the "key" or "btn" type. It is also lazily computed
    /// so it does not bother users who don't use type-level maps.
    pub static ref BTN_CODE_RANGES: Vec<std::ops::Range<u16>> = {
        // Compute for each valid code whether (EV_KEY, code) is called btn:something or not.
        let mut is_btn_vec: Vec<(u16, bool)> = EVENT_CODES.iter()
            .filter(|(_, code)| code.ev_type().is_key())
            .map(|((typename, _codename), code)| (code.code(), typename == "btn"))
            .collect();
        is_btn_vec.sort_unstable();

        // Detect consecutive ranges of (code, true) in `is_btn_vec`.
        let mut ranges: Vec<std::ops::Range<u16>> = Vec::new();
        let mut range_start: Option<u16> = None;

        for &(code, is_btn) in &is_btn_vec {
            if is_btn {
                if range_start.is_none() {
                    range_start = Some(code);
                }
            } else if let Some(start) = range_start {
                // Remember that ranges are exclusive.
                ranges.push(start .. code);
                range_start = None;
            }
        }

        if let Some(start) = range_start {
            if let Some((last_code, true)) = is_btn_vec.last() {
                ranges.push(start .. last_code + 1)
            }
        }

        ranges
    };
}

pub fn event_type_get_max(ev_type: EventType) -> Option<u16> {
    let result = unsafe { libevdev::libevdev_event_type_get_max(ev_type.into()) };
    result.try_into().ok()
}

pub fn type_name(ev_type: EventType) -> Cow<'static, str> {
    for (name, &type_) in EVENT_TYPES.iter() {
        if ev_type == type_ {
            return Cow::from(name);
        }
    }

    Cow::from(format!("{}", u16::from(ev_type)))
}

pub fn virtual_type_name(virtual_type: VirtualEventType) -> Cow<'static, str> {
    match virtual_type {
        VirtualEventType::Key => Cow::from(VirtualEventType::KEY),
        VirtualEventType::Button => Cow::from(VirtualEventType::BUTTON),
        VirtualEventType::Other(ev_type) => type_name(ev_type),
    }
}

pub fn event_name(code: EventCode) -> Cow<'static, str> {
    match EVENT_NAMES.get(&code) {
        Some(name) => Cow::from(name),
        None => {
            let type_name = virtual_type_name(code.virtual_ev_type());
            Cow::from(format!("{}:%{}", type_name, code.code()))
        },
    }
}

/// Returns true if this code is of type EV_KEY with a BTN_* code.
pub fn is_button_code(code: EventCode) -> bool {
    code.ev_type().is_key() &&
        BTN_CODE_RANGES.iter().any(|range| range.contains(&code.code()))
}

/// Returns whether this event is an multitouch event.
pub fn is_abs_mt(code: EventCode) -> bool {
    code.ev_type().is_abs() && event_name(code).starts_with("abs:mt_")
}

/// Parses an event type by name like "key" or number like "%1".
pub fn event_type(name: &str) -> Result<EventType, ArgumentError> {
    if let Some(&ev_type) = EVENT_TYPES.get(name) {
        return Ok(ev_type);
    }

    let name_numstr = match name.strip_prefix('%') {
        Some(string) => string,
        None => return Err(ArgumentError::new(format!(
            "Unknown event type \"{}\".", name
        ))),
    };

    let type_u16: u16 = match name_numstr.parse() {
        Ok(code) => code,
        Err(_) => return Err(ArgumentError::new(format!(
            "Cannot interpret \"{}\" as a nonnegative integer.", name_numstr
        ))),
    };

    if type_u16 <= EV_MAX {
        for &ev_type in EVENT_TYPES.values() {
            if type_u16 == ev_type.into() {
                return Ok(ev_type);
            }
        }

        Err(ArgumentError::new(format!(
            "No event type with numeric value {} exists.",
            type_u16
        )))
    } else {
        Err(ArgumentError::new(format!(
            "Event type {} exceeds the maximum value of {} defined by EV_MAX.",
            type_u16, EV_MAX
        )))
    }
}

/// Parses an event code by name like "key","a" or by name-number pair like "key","%35".
pub fn event_code(type_name: &str, code_name: &str) -> Result<EventCode, ArgumentError> {
    // Check whether the type and code can be interpreted as names.
    if let Some(&code) = EVENT_CODES.get(&(type_name.to_string(), code_name.to_string())) {
        return Ok(code)
    }

    // Check for a (name, number) pair.
    let code_name_numstr = match code_name.strip_prefix('%') {
        Some(string) => string,
        None => {
            // Return a specifically helpful error if the user entered a number as code, e.g.
            // tried btn:300 instead of btn:%300.
            if code_name.parse::<u16>().is_ok() {
                return Err(ArgumentError::new(format!(
                    "Unknown event code \"{}:{}\". (Tip: if you meant to specify an event of type {} and a code of numeric value {}, then you need to add a % prefix like this: \"{}:%{}\")", type_name, code_name, type_name, code_name, type_name, code_name
                )));
            } else {
                return Err(ArgumentError::new(format!(
                    "Unknown event code \"{}:{}\".", type_name, code_name
                )));
            }
        }
    };

    let ev_type = event_type(type_name)?;
    let ev_type_max = match event_type_get_max(ev_type) {
        Some(max) => max,
        None => return Err(ArgumentError::new(format!(
            "No valid event codes exist for event type {}.", type_name,
        ))),
    };
    let code_u16: u16 = match code_name_numstr.parse() {
        Ok(code) => code,
        Err(_) => return Err(ArgumentError::new(format!(
            "Cannot interpret \"{}\" as a nonnegative integer.", code_name_numstr
        ))),
    };

    if code_u16 <= ev_type_max {
        let code = EventCode::new(ev_type, code_u16);
        if ! EVENT_NAMES.contains_key(&code) {
            crate::utils::warn_once(format!(
                "Warning: no event code {}:{} is known to exist. Working with such events may yield unexpected results.", type_name, code_name
            ));
        }

        let virtual_type = code.virtual_ev_type();
        if (type_name == VirtualEventType::KEY && virtual_type != VirtualEventType::Key)
            || (type_name == VirtualEventType::BUTTON && virtual_type != VirtualEventType::Button)
        {
            crate::utils::warn_once(format!(
                "Info: {}:{} shall be interpreted as {}.", type_name, code_name, event_name(code)
            ))
        }
        
        Ok(code)
    } else {
        Err(ArgumentError::new(format!(
            "Event code {} exceeds the maximum value of {} for events of type {}.",
            code_u16, ev_type_max, type_name 
        )))
    }
}

pub const EV_ABS: u16 = libevdev::EV_ABS as u16;
pub const EV_SYN: u16 = libevdev::EV_SYN as u16;
pub const EV_REP: u16 = libevdev::EV_REP as u16;
pub const EV_KEY: u16 = libevdev::EV_KEY as u16;
pub const EV_MSC: u16 = libevdev::EV_MSC as u16;
pub const EV_MAX: u16 = libevdev::EV_MAX as u16;

pub const REP_DELAY: u16 = libevdev::REP_DELAY as u16;
pub const REP_PERIOD: u16 = libevdev::REP_PERIOD as u16;
pub const MSC_SCAN: u16 = libevdev::MSC_SCAN as u16;

/// Returns an iterator over all event types that fall within EV_MAX,
/// whether those types are named or not.
pub fn event_types() -> impl Iterator<Item=EventType> {
    (0 ..= EV_MAX).map(EventType::new)
}

/// Returns an iterator over all event codes that fall within their
/// types maximum, whether those types are named or not.
pub fn event_codes_for(ev_type: EventType) -> impl Iterator<Item=EventCode> {
    // I tested: it is possible to write events like key:max to event devices.
    event_type_get_max(ev_type).into_iter().flat_map(move |max|
        (0 ..= max).map(move |code| EventCode::new(ev_type, code))
    )
}

#[test]
fn unittest() {
    // Since the is_abs_mt function depends on the user-facing representation we use for events,
    // this test makes sure it doesn't accidentally break if we change out naming scheme.
    assert!(is_abs_mt(EventCode::new(EventType::ABS, 0x35)));
    assert!(!is_abs_mt(EventCode::new(EventType::ABS, 0x01)));
    assert!(!is_abs_mt(EventCode::new(EventType::KEY, 0x35)));
}
0707010000003A000081A4000000000000000000000001647DB23000001689000000000000000000000000000000000000001D00000000evsieve-1.4.0~0/src/error.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::io;
use std::fmt;
use std::fmt::Write;

/// A trait for errors similiar in concept to anyhow::Error.
pub trait Context {
    fn context(&self) -> &[String];
    fn with_context<T: Into<String>>(self, context: T) -> Self;
    /// Like Context::with_context(), but lazily evaluated.
    fn with_context_of<F: Fn() -> String>(self, context: F) -> Self;
    fn print_err(self);
}

fn format_error_with_context(f: &mut fmt::Formatter, err_context: Vec<String>, err_msg: String) -> fmt::Result {
    let mut context_collapsed: Vec<String> = err_context;
    context_collapsed.push(err_msg);
    let mut output: String = String::new();

    for (indent, context_line) in context_collapsed.into_iter().enumerate() {
        for _ in 0..indent {
            write!(output, "    ")?;
        }
        writeln!(output, "{}", context_line)?;
    }

    // Remove the trailing newline.
    output.pop();
    write!(f, "{}", output)
}

macro_rules! context_error {
    ($name:ident) => {
        #[derive(Debug)]
        pub struct $name {
            context: Vec<String>,
            message: String,
        }
        impl $name {
            pub fn new(message: impl Into<String>) -> Self {
                Self { message: message.into(), context: Vec::new() }
            }
        }
        impl Context for $name {
            fn context(&self) -> &[String] {
                &self.context
            }

            fn with_context<T: Into<String>>(mut self, context: T) -> Self {
                self.context.insert(0, context.into());
                self
            }

            fn with_context_of<F: Fn() -> String>(self, context: F) -> Self {
                self.with_context(context())
            }

            fn print_err(self) {
                eprintln!("{}", self);
            }
        }
    };
}

macro_rules! runtime_errors {
    ( $( $name:ident ),* ) => {
        $(
            context_error!($name);
        )*

        #[derive(Debug)]
        pub enum RuntimeError {
            $(
                $name ( $name ),
            )*
        }

        impl Context for RuntimeError {
            fn with_context<T: Into<String>>(self, context: T) -> RuntimeError {
                match self {
                    $(
                        RuntimeError::$name(error) => RuntimeError::$name(error.with_context(context.into())),
                    )*
                }
            }

            fn with_context_of<T: Fn() -> String>(self, context: T) -> RuntimeError {
                match self {
                    $(
                        RuntimeError::$name(error) => RuntimeError::$name(error.with_context(context())),
                    )*
                }
            }

            fn context(&self) -> &[String] {
                match self {
                    $(
                        RuntimeError::$name(error) => error.context(),
                    )*
                }
            }

            fn print_err(self) {
                match self {
                    $(
                        RuntimeError::$name(error) => error.print_err(),
                    )*
                }
            }
        }

        impl fmt::Display for RuntimeError {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                match &self {
                    $(
                        RuntimeError::$name(error) => write!(f, "{}", error),
                    )*
                }
            }
        }

        $(
            impl From<$name> for RuntimeError {
                fn from(error: $name) -> RuntimeError {
                    RuntimeError::$name(error)
                }
            }
        )*
    };
}

macro_rules! display_error {
    ($name:ident, $header:expr) => {
        impl fmt::Display for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                let message_lowercase = first_letter_to_lowercase(self.message.clone());
                let err_message = format!($header, message_lowercase);
                format_error_with_context(f, self.context().to_owned(), err_message)
            }
        }
    };
}

impl SystemError {
    pub fn os_with_context(context: impl Into<String>) -> SystemError {
        SystemError::from(std::io::Error::last_os_error())
            .with_context(context)
    }
}

runtime_errors!(ArgumentError, InternalError, SystemError);
display_error!(ArgumentError, "Invalid argument: {}");
display_error!(InternalError, "Internal error: {}");
display_error!(SystemError, "System error: {}");

impl From<io::Error> for SystemError {
    fn from(error: io::Error) -> SystemError {
        SystemError::new(format!("{}", error))
    }
}

impl From<io::Error> for RuntimeError {
    fn from(error: io::Error) -> RuntimeError {
        SystemError::from(error).into()
    }
}

impl<T, E> Context for Result<T, E> where E: Context {
    fn with_context<S: Into<String>>(self, context: S) -> Self {
        match self {
            Ok(value) => Ok(value),
            Err(error) => Err(error.with_context(context.into())),
        }
    }

    fn with_context_of<F: Fn() -> String>(self, context: F) -> Self {
        match self {
            Ok(value) => Ok(value),
            Err(error) => Err(error.with_context_of(context)),
        }
    }

    fn context(&self) -> &[String] {
        match self {
            Ok(_) => &[],
            Err(error) => error.context(),
        }
    }

    fn print_err(self) {
        if let Err(error) = self {
            error.print_err();
        }
    }
}

fn first_letter_to_lowercase(mut string: String) -> String {
    if let Some(first_char) = string.get_mut(0..1) {
        first_char.make_ascii_lowercase();
    }
    string
}
0707010000003B000081A4000000000000000000000001647DB2300000159A000000000000000000000000000000000000001D00000000evsieve-1.4.0~0/src/event.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::fmt;
use crate::domain::Domain;
use crate::ecodes;

pub type EventValue = i32;
pub type Channel = (EventCode, Domain);

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct EventType(u16);

impl EventType {
    pub const KEY: EventType = EventType(ecodes::EV_KEY);
    pub const ABS: EventType = EventType(ecodes::EV_ABS);
    pub const REP: EventType = EventType(ecodes::EV_REP);
    pub const SYN: EventType = EventType(ecodes::EV_SYN);
    pub const MSC: EventType = EventType(ecodes::EV_MSC);

    pub fn is_key(self) -> bool {
        self == EventType::KEY
    }
    pub fn is_abs(self) -> bool {
        self == EventType::ABS
    }
    pub fn is_rep(self) -> bool {
        self == EventType::REP
    }
    pub fn is_syn(self) -> bool {
        self == EventType::SYN
    }
}

impl EventType {
    pub const fn new(value: u16) -> EventType {
        debug_assert!(value <= ecodes::EV_MAX);
        EventType(value)
    }
}

/// Internally, all event codes have their respective type attached to them. This avoids
/// logic errors, since some codes make no sense for types other than the one they were
/// intended for, and various FFI calls may emit undefined behaviour if we provide them
/// with invalid event types.
///
/// Upholds invariant: the code is a valid code for this type.
/// Creating an EventCode with a nonexistent (type, code) pair is considered undefined behaviour.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct EventCode {
    ev_type: EventType,
    code: u16,
}

impl EventCode {
    pub const MSC_SCAN: EventCode = EventCode {
        ev_type: EventType::MSC,
        code: ecodes::MSC_SCAN,
    };

    pub const fn new(ev_type: EventType, code: u16) -> EventCode {
        EventCode { ev_type, code }
    }

    pub const fn ev_type(self) -> EventType {
        self.ev_type
    }

    pub const fn code(self) -> u16 {
        self.code
    }

    pub fn virtual_ev_type(self) -> VirtualEventType {
        if self.ev_type().is_key() {
            if ecodes::is_button_code(self) {
                VirtualEventType::Button
            } else {
                VirtualEventType::Key
            }
        } else {
            VirtualEventType::Other(self.ev_type())
        }
    }
}

impl From<EventType> for u16 {
    fn from(ev_type: EventType) -> u16 {
        ev_type.0
    }
}

impl From<EventType> for u32 {
    fn from(ev_type: EventType) -> u32 {
        u16::from(ev_type) as u32
    }
}

/// In the kernel, the type EV_KEY is used for both keys like key:a and buttons like btn:left.
/// This could confuse the user if a "--map key" argument were to also match btn:something events.
///
/// To resolve this, we introduce a virtual event type which is equivalent to the kernel type
/// except that EV_KEY is split into two different types, one for events with EV_KEY, KEY_* codes
/// and one for events with EV_KEY, BTN_* codes.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum VirtualEventType {
    Key,
    Button,
    Other(EventType),
}

impl VirtualEventType {
    pub const KEY: &'static str = "key";
    pub const BUTTON: &'static str = "btn";

    pub fn ev_type(self) -> EventType {
        match self {
            VirtualEventType::Key | VirtualEventType::Button => EventType::KEY,
            VirtualEventType::Other(ev_type) => ev_type,
        }
    }
}

#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Event {
    pub code: EventCode,
    pub value: EventValue,

    /// The value this event had the last time it was emitted by a device.
    pub previous_value: EventValue,

    pub domain: Domain,
    pub namespace: Namespace,
}

impl Event {
    pub fn new(code: EventCode,
               value: EventValue,
               previous_value: EventValue,
               domain: Domain,
               namespace: Namespace
    ) -> Event {
        Event { code, value, previous_value, domain, namespace }
    }

    pub fn with_domain(mut self, new_domain: Domain) -> Event {
        self.domain = new_domain;
        self
    }

    pub fn ev_type(self) -> EventType {
        self.code.ev_type()
    }

    pub fn channel(self) -> Channel {
        (self.code, self.domain)
    }
}

impl fmt::Display for Event {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = ecodes::event_name(self.code);
        write!(f, "{}:{}", name, self.value)
    }
}

impl fmt::Debug for Event {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = ecodes::event_name(self.code);
        write!(f, "{}:{}..{}@{:?}", name, self.previous_value, self.value, self.domain)
    }
}


/// Namespaces are an internal concept that is not visible to the user. They are like domains, but
/// then on a higher level such that even a filter with an empty domain cannot match events within a
/// different namespace.
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
pub enum Namespace {
    /// This event was generated by an input device and has not yet entered the processing stream
    /// from the end user's perspective. It is not affected by any `StreamEntry` except `StreamEntry::Input`.
    Input,
    /// This event is in the processing stream.
    User,
    /// This event was generated by --map yield or similar. It is not affected by any `StreamEntry`
    /// except for `StreamEntry::Output`.
    Yielded,
    /// This event was caught by an --output and shall now be sent to an output device. It is not
    /// affected by any StreamEntry.
    Output,
}0707010000003C000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001700000000evsieve-1.4.0~0/src/io0707010000003D000081A4000000000000000000000001647DB23000001CFA000000000000000000000000000000000000002000000000evsieve-1.4.0~0/src/io/epoll.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::{Context, SystemError};
use crate::io::fd::{OwnedFd, HasFixedFd};
use std::collections::HashMap;
use std::os::unix::io::{AsRawFd};

pub const INDEFINITE_TIMEOUT: i32 = -1;

/// Like a file descriptor, that identifies a file registered in this Epoll.
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct FileIndex(u64);

/// The epoll is responsible for detecting which input devices have events available.
/// The evsieve program spends most of its time waiting on Epoll::poll, which waits until
/// some input device has events available.
/// 
/// It also keeps track of when input devices unexpectedly close.
pub struct Epoll<T: HasFixedFd> {
    fd: OwnedFd,
    files: HashMap<FileIndex, T>,
    /// A counter, so every file registered can get an unique index in the files map.
    counter: u64,
}

/// Represents a result that an Epoll may return.
pub enum Message {
    Ready(FileIndex),
    /// Represents a EPOLLERR result.
    Broken(FileIndex),
    /// Represents a EPOLLHUP result that is not simultaneously EPOLLERR.
    Hup(FileIndex),
}

impl<T: HasFixedFd> Epoll<T> {
    pub fn new() -> Result<Epoll<T>, SystemError> {
        let epoll_fd = unsafe {
            OwnedFd::from_syscall(libc::epoll_create1(libc::EPOLL_CLOEXEC))
                .with_context("While trying to create an epoll instance:")?
        };

        Ok(Epoll {
            fd: epoll_fd,
            files: HashMap::new(),
            counter: 0,
        })
    }

    fn get_unique_index(&mut self) -> FileIndex {
        self.counter += 1;
        FileIndex(self.counter)
    }

    /// # Safety
    /// The file must return a valid raw file descriptor.
    pub fn add_file(&mut self, file: T) -> Result<FileIndex, SystemError> {
        let index = self.get_unique_index();
        let file_fd = file.as_raw_fd();

        // Sanity check: make sure we don't add a file that already belongs to this epoll.
        if self.files.values().any(|opened_file| opened_file.as_raw_fd() == file_fd) {
            return Err(SystemError::new("Cannot add a file to an epoll that already belongs to said epoll."));
        }
        self.files.insert(index, file);

        // We set the data to the index of said file, so we know which file is ready for reading.
        let mut event = libc::epoll_event {
            events: libc::EPOLLIN as u32,
            u64: index.0,
        };

        let result = unsafe { libc::epoll_ctl(
            self.fd.as_raw_fd(),
            libc::EPOLL_CTL_ADD,
            file_fd,
            &mut event,
        ) };

        if result < 0 {
            Err(SystemError::os_with_context("While adding a device to an epoll instance:"))
        } else {
            Ok(index)
        }
    }

    /// Returns an iterator over all files belonging to this epoll.
    pub fn files(&self) -> impl Iterator<Item=&T> {
        self.files.values()
    }

    pub fn contains_index(&self, index: FileIndex) -> bool {
        self.files.contains_key(&index)
    }

    pub fn get(&self, index: FileIndex) -> Option<&T> {
        self.files.get(&index)
    }

    pub fn get_mut(&mut self, index: FileIndex) -> Option<&mut T> {
        self.files.get_mut(&index)
    }

    /// Removes a file specified by an index from this epoll.
    pub fn remove(&mut self, index: FileIndex) -> Option<T> {
        let file = match self.files.remove(&index) {
            Some(file) => file,
            None => return None,
        };

        let result = unsafe { libc::epoll_ctl(
            self.fd.as_raw_fd(),
            libc::EPOLL_CTL_DEL,
            file.as_raw_fd(),
            std::ptr::null_mut(),
        )};

        if result < 0 {
            match std::io::Error::last_os_error().raw_os_error()
                    .expect("An unknown error occurred while removing a file from an epoll.") {
                // This file was not registered by this epoll.
                libc::ENOENT => eprintln!("Internal error: attempted to remove a device from an epoll that's not registered with it."),
                // There was not enough memory to carry out this operation.
                libc::ENOMEM => panic!("Out of kernel memory."),
                // The other error codes should never happen or indicate fundamentally broken invariants.
                _ => panic!("Failed to remove a file from an epoll: {}", std::io::Error::last_os_error()),
            }
        }

        Some(file)
    }

    fn poll_raw(&mut self, timeout: i32) -> Result<Vec<libc::epoll_event>, std::io::Error> {
        // The number 8 was chosen arbitrarily.
        let max_events: i32 = std::cmp::min(self.files.len(), 8) as i32;
        let mut events: Vec<libc::epoll_event> = (0 .. max_events).map(|_| libc::epoll_event {
            // The following values don't matter since the kernel will overwrite them anyway.
            // We're just initialzing them to make the compiler happy.
            events: 0, u64: 0
        }).collect();

        let result = unsafe {
            libc::epoll_wait(
                self.fd.as_raw_fd(),
                events.as_mut_ptr(),
                max_events,
                timeout,
            )
        };

        if result < 0 {
            Err(std::io::Error::last_os_error())
        } else {
            let num_fds = result as usize;
            Ok(events[0..num_fds].to_owned())
        }
    }

    /// Tries to read all events from all ready devices. Returns a vector containing all events read.
    /// If a device reports an error, said device is removed from self and also returned.
    ///
    /// Timeout means the same thing as in the `epoll_wait()` syscall: time in milliseconds,
    /// -1 means forever, 0 means instant return.
    pub fn poll(&mut self, timeout: i32) -> Result<impl Iterator<Item=Message>, SystemError> {
        let events = loop {
            match self.poll_raw(timeout) {
                Ok(events) => break events,
                Err(error) => match error.kind() {
                    std::io::ErrorKind::Interrupted => continue,
                    _ => return Err(error.into())
                }
            }
        };

        // Create a list of which devices are ready and which are broken.
        let mut messages: Vec<Message> = Vec::new();

        for event in events {
            let file_index = FileIndex(event.u64);

            if event.events & libc::EPOLLIN as u32 != 0 {
                messages.push(Message::Ready(file_index));
            }
            if event.events & libc::EPOLLERR as u32 != 0 {
                messages.push(Message::Broken(file_index));
            } else if event.events & libc::EPOLLHUP as u32 != 0 {
                messages.push(Message::Hup(file_index));
            }
        }

        Ok(messages.into_iter())
    }

    /// Returns whether currently any files are opened under this epoll.
    pub fn is_empty(&self) -> bool {
        self.files.is_empty()
    }
}

impl<T: HasFixedFd> std::ops::Index<FileIndex> for Epoll<T> {
    type Output = T;
    fn index(&self, index: FileIndex) -> &Self::Output {
        &self.files[&index]
    }
}

impl<T: HasFixedFd> std::ops::IndexMut<FileIndex> for Epoll<T> {
    fn index_mut(&mut self, index: FileIndex) -> &mut Self::Output {
        self.files.get_mut(&index).expect("Internal error: attempt to retrieve a file that does not belong to this epoll.")
    }
}
0707010000003E000081A4000000000000000000000001647DB2300000130C000000000000000000000000000000000000001D00000000evsieve-1.4.0~0/src/io/fd.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::SystemError;
use std::io::Read;
use std::os::unix::io::{FromRawFd, AsRawFd, RawFd};

/// A wrapper around a file descriptor that calls `libc::close` on the descriptor when it is dropped.
/// Guarantees that the file descriptor it owns is valid for the lifetime of this structure.
#[repr(transparent)]
pub struct OwnedFd(RawFd);

impl OwnedFd {
    /// Takes ownership of a given file descriptor.
    /// 
    /// # Safety
    /// The file descriptor must be valid. Furthermore, it must not be closed by anything else during
    /// the lifetime of this struct.
    /// 
    /// # Panics
    /// Panics if the passed fd is below zero.
    pub unsafe fn new(fd: RawFd) -> OwnedFd {
        OwnedFd::from_raw_fd(fd)
    }

    /// To be called on the result of a syscall that returns a file descriptor. Takes ownership of
    /// the given file descriptor if positive, otherwise returns the last OS error.
    ///
    /// # Safety
    /// The file descriptor must be valid or negative. Furthermore, it must not be closed by anything
    /// else during the lifetime of this struct.
    pub unsafe fn from_syscall(fd: libc::c_int) -> Result<OwnedFd, SystemError> {
        if fd >= 0 {
            Ok(OwnedFd::new(fd))
        } else {
            Err(std::io::Error::last_os_error().into())
        }
    }

    /// # Safety
    /// This file descriptor should be applicable to the read() system call.
    pub unsafe fn readable(self) -> ReadableFd {
        ReadableFd(self)
    }
}

impl FromRawFd for OwnedFd {
    unsafe fn from_raw_fd(fd: RawFd) -> Self {
        if fd < 0 {
            panic!("A file descriptor below zero was encountered. This suggests an unhandled I/O error.");
        }
        OwnedFd(fd)
    }
}

impl AsRawFd for OwnedFd {
    fn as_raw_fd(&self) -> RawFd {
        self.0
    }
}

impl Drop for OwnedFd {
    fn drop(&mut self) {
        unsafe { libc::close(self.as_raw_fd()) };
    }
}

/// Like OwnedFd, but implements the `Read` trait.
pub struct ReadableFd(OwnedFd);

impl AsRawFd for ReadableFd {
    fn as_raw_fd(&self) -> RawFd {
        self.0.as_raw_fd()
    }
}

impl Read for ReadableFd {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        let res = unsafe { libc::read(
            self.as_raw_fd(), buf.as_mut_ptr() as *mut libc::c_void, buf.len())
        };
        if res < 0 {
            Err(std::io::Error::last_os_error())
        } else {
            Ok(res as usize)
        }

    }
}

/// # Safety
///
/// An unsafe marker trait: if a structure implements this trait, it promises that its file descriptor
/// will cannot be changed by functions that do not own the structure, i.e. no function that takes a
/// (mutable) reference is allowed to modify the structure in a way that makes as_raw_fd() return a
/// different value.
///
/// Furthermore, the result returned by AsRawFd must be guaranteed to be valid for the duration of the
/// structure.
///
/// Changing the file descriptor of a struct with this trait through a reference may invoke undefined
/// behaviour. Unsafe code may assume that the file descriptor does not change even if it hands out an
/// &mut reference to a structure with HasFixedFd.
///
/// This constraint is unfortunately unsound, because even if in a given module there is no code that
/// allows changing a file descriptor through &mut, it is always possible to construct a second instance
/// of a certain struct and then std::men::swap() them. This could happen anywhere in safe code.
///
/// I really don't like this current approach and of course this attitude towards unsafety
/// would be unacceptable in a library, but I don't see a way around it other than (1) moving away from
/// epoll() towards poll(), possibly introducing a performance regression, (2) decoupling the file
/// descriptors from the surrounding data, which increases code complexity and probably introduces a
/// lot more potential for unsafety, (3) adding additional verification code to the `Epoll`class,
/// which comes at a performance penalty.
///
/// Maybe one day I'll start using poll() if benchmarks show that it has no measurable performance
/// impact. Other than that, I think that putting up with this trait is just the least of the many
/// possible evils.
///
/// To be clear: just because a certain structure X implements this trait, does not mean that any
/// structure containing X has that trait as well. For example, OwnedFd implements it because there
/// is no (safe) function that modifies OwnedFd in a way that changes its file descriptor, but any
/// struct containing OwnedFd still needs to implement it to guarantee that it will not swap out its
/// OwnedFd for another OwnedFd.
pub unsafe trait HasFixedFd : AsRawFd {}

unsafe impl HasFixedFd for OwnedFd {}
unsafe impl HasFixedFd for ReadableFd {}
0707010000003F000081A4000000000000000000000001647DB23000001B0B000000000000000000000000000000000000001F00000000evsieve-1.4.0~0/src/io/fifo.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::fmt::Display;
use std::io::{Read};
use std::os::unix::io::{RawFd, AsRawFd};
use std::ffi::CString;
use std::path::{Path, PathBuf};

use crate::error::{SystemError, Context};
use crate::io::fd::{OwnedFd, ReadableFd};

use super::fd::HasFixedFd;

// TODO: LOW-PRIORITY: Move this structure elsewhere.
struct OwnedPath(PathBuf);

pub trait LineRead : AsRawFd {
    fn read_lines(&mut self) -> Result<Vec<String>, std::io::Error>;
}

impl OwnedPath {
    pub fn new(path: PathBuf) -> OwnedPath {
        OwnedPath(path)
    }
}

impl Display for OwnedPath {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0.to_string_lossy())
    }
}

impl Drop for OwnedPath {
    fn drop(&mut self) {
        let _ = std::fs::remove_file(&self.0);
    }
}

pub struct LineReader<T: Read> {
    /// The device/file/pipe/whatever to read data from.
    source: T,
    /// Bytes that have been read from the source, but not yet emitted to the receiver.
    cached_data: Vec<u8>,
}

impl<T: Read> LineReader<T> {
    pub fn new(source: T) -> Self {
        LineReader {
            source, cached_data: Vec::new()
        }
    }

    /// Performs a single read() call on the underlying source, which may result into reading
    /// zero or more lines in total.
    pub fn read_lines(&mut self) -> Result<Vec<String>, std::io::Error> {
        let mut buf: [u8; libc::PIPE_BUF] = [0; libc::PIPE_BUF];
        let num_bytes_read = match self.source.read(&mut buf) {
            Ok(res) => res,
            Err(error) => match error.kind() {
                std::io::ErrorKind::Interrupted | std::io::ErrorKind::WouldBlock
                    => return Ok(Vec::new()),
                _ => return Err(error),
            }
        };

        self.cached_data.extend_from_slice(&buf[0 .. num_bytes_read]);
        let mut data = self.cached_data.as_slice();
        let mut result = Vec::new();

        // Read lines from the cached data until no lines are left anymore.
        const NEWLINE_DENOMINATOR: u8 = 0xA; // The \n ASCII new line denominator.
        while let Some(newline_index) = linear_search(data, &NEWLINE_DENOMINATOR) {
            let before_newline = &data[0 .. newline_index];
            let after_newline = if data.len() > newline_index + 1 {
                &data[newline_index + 1 .. data.len()]
            } else {
                &[]
            };

            match String::from_utf8(before_newline.to_owned()) {
                Ok(string) => result.push(string),
                Err(_) => {
                    eprintln!("Error: received non-UTF-8 data. Data ignored.");
                }
            }

            data = after_newline;
        }

        self.cached_data = data.to_owned();

        Ok(result)
    }

    pub fn get_buffered_data(&self) -> &[u8] {
        &self.cached_data
    }

    pub fn get_ref(&self) -> &T {
        &self.source
    }
}

/// Represents the reading end of a Fifo that resides on the file system.
/// The file on the filesystem is deleted when the Fifo is dropped.
pub struct Fifo {
    path: OwnedPath,
    reader: LineReader<ReadableFd>,
}

impl Fifo {
    pub fn create(path: &str) -> Result<Fifo, SystemError> {
        let path_cstring = CString::new(path)
            .map_err(|_| SystemError::new("Path may not contain any nul bytes."))?;

        let res = unsafe { libc::mkfifo(path_cstring.as_ptr(), 0o600) };
        if res < 0 {
            return Err(SystemError::os_with_context(format!(
                "While attempting to create a fifo at {}:", path
            )));
        }

        let owned_path = OwnedPath::new(path.into());
        let fd = unsafe {
            // Workaround suggested by:
            //     https://stackoverflow.com/questions/22021253/poll-on-named-pipe-returns-with-pollhup-constantly-and-immediately
            //
            // You might think that we should open this epoll with O_RDONLY because we only ever read
            // from it. However, the Linux kernel devs, in their infinite wisdom, decided that whenever
            // an FIFO gets closed by its last writer, it generates an EPOLLHUP event which is not
            // cleared after being read from the epoll, and which does not seem to be clearable by any
            // less-than-farfetched means. (Or at least, I haven't found a good way to clear it yet.)
            // Consequently, a level-triggered epoll will immediately return from any subsequent
            // `epoll_wait()` calls, resulting in a busy loop consuming 100% CPU.
            //
            // Other than switching to an edge-triggered epoll (which is another whole can of worms)
            // the best workaround I found seems to be to open the FIFO for writing ourselves, which
            // ensures that the last writer (us) never closes the FIFO and thereby preventing that
            // EPOLLHUP event from happening.
            //
            // Hence the O_RDWR mode.
            OwnedFd::from_syscall(
                libc::open(path_cstring.as_ptr(), libc::O_RDWR | libc::O_NONBLOCK)
            ).with_context_of(|| format!(
                "While trying to open the fifo at {}:", path
            ))?
            .readable()
        };
        let reader = LineReader::new(fd);

        Ok(Fifo { path: owned_path, reader })
    }

    pub fn path(&self) -> &Path {
        &self.path.0
    }
}

impl LineRead for Fifo {
    /// Returns all lines that are ready for this Fifo.
    /// The lines shall not end at a \n character.
    /// This function returns all lines that are available and shall not return any more lines
    /// until the epoll says that it ise ready again.
    fn read_lines(&mut self) -> Result<Vec<String>, std::io::Error> {
        let lines = self.reader.read_lines()?;

        if ! self.reader.get_buffered_data().is_empty() {
            // TODO: FEATURE(control-fifo) this blatantly assumes that the Fifo is used as command fifo.
            // TODO: FEATURE(control-fifo) Also, this somehow does not work. Figure out why.
            let partial_command = String::from_utf8_lossy(self.reader.get_buffered_data());
            eprintln!("Error: received a command \"{}\" that was not terminated by a newline character. All commands must be terminated by newline characters.", partial_command);
        }

        Ok(lines)
    }
}

impl AsRawFd for Fifo {
    fn as_raw_fd(&self) -> RawFd {
        self.reader.get_ref().as_raw_fd()
    }
}

unsafe impl HasFixedFd for Fifo {}

/// Returns the index of the first instance of `search_elem` in the provided slice, or `None`
/// if it is not found in said slice.
fn linear_search<T : Eq>(container: &[T], search_elem: &T) -> Option<usize> {
    for (index, elem) in container.iter().enumerate() {
        if elem == search_elem {
            return Some(index)
        }
    }

    None
}
07070100000040000081A4000000000000000000000001647DB230000039D2000000000000000000000000000000000000002000000000evsieve-1.4.0~0/src/io/input.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::ffi::{CStr, CString};
use std::fs::{File, OpenOptions};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use std::mem::MaybeUninit;
use crate::bindings::libevdev;
use crate::event::{Event, EventType, EventValue, EventCode, Namespace};
use crate::domain::Domain;
use crate::capability::{AbsInfo, Capabilities, InputCapabilites, RepeatInfo};
use crate::ecodes;
use crate::predevice::{GrabMode, PersistMode, PreInputDevice};
use crate::error::{SystemError, Context};
use crate::persist::blueprint::Blueprint;
use crate::time::Instant;

use super::fd::HasFixedFd;

pub fn open_and_query_capabilities(pre_input_devices: Vec<PreInputDevice>)
    -> Result<(Vec<InputDevice>, InputCapabilites), SystemError>
{
    let mut input_devices = pre_input_devices.into_iter().map(
        |device| {
            let device_path = device.path.clone();
            InputDevice::open(device)
                .map_err(SystemError::from)
                .with_context(format!("While opening the device \"{}\":", device_path.display()))
    }).collect::<Result<Vec<InputDevice>, SystemError>>()?;

    // Return an error if a device with grab=force cannot be grabbed.
    for device in &mut input_devices {
        device.grab_if_desired()?;
    }

    // Precompute the capabilities of the input devices.
    let mut capabilities: InputCapabilites = InputCapabilites::new();
    for device in &input_devices {
        // TODO: LOW-PRIORITY: Consider using an Rc instead of a clone.
        capabilities.insert(device.domain, device.capabilities.clone());
    }

    Ok((input_devices, capabilities))
}

/// Represents a name as reported by libevdev_get_name().
pub type InputDeviceName = CString;

pub struct InputDevice {
    /// The file owns the file descriptor to the input device. Beware: InputDevice implements HasFixedFd.
    file: File,
    path: PathBuf,
    evdev: *mut libevdev::libevdev,

    capabilities: Capabilities,

    /// The name as reported by libevdev_get_name().
    name: InputDeviceName,

    /// Whether and how the user has requested this device to be grabbed.
    grab_mode: GrabMode,
    /// Whether the device is actually grabbed.
    grabbed: bool,

    /// The domain, though not part of libevdev, is a handy tag we use
    /// to track which device emitted the events.
    domain: Domain,

    /// Maps (type, code) pairs to the last known value of said pair.
    state: HashMap<EventCode, EventValue>,

    /// What should happen if this device disconnects.
    persist_mode: PersistMode,
}

impl InputDevice {
    /// Opens an input device from a given path.
    ///
    /// Does not grab the device even if grab=force is specified. You must do that manually later
    /// by calling grab_if_desired().
    pub fn open(pre_device: PreInputDevice) -> Result<InputDevice, SystemError> {
        let path = pre_device.path;
        let domain = pre_device.domain;

        // Open the file itself.
        let file = OpenOptions::new()
            .read(true)
            // O_CLOEXEC is already set by default in the std source code, but I'm providing it
            // anyway to clearly signify we _need_ that flag.
            .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
            .open(&path)?;

        // Turn the file into an evdev instance.
        let mut evdev: *mut libevdev::libevdev = std::ptr::null_mut();
        let res = unsafe {
            libevdev::libevdev_new_from_fd(file.as_raw_fd(), &mut evdev)
        };
        if res < 0 {
            return Err(SystemError::new(
                format!("Failed to open a libevdev instance: {}.", path.to_string_lossy())
            ));
        }

        let capabilities = unsafe { get_capabilities(evdev) };
        let state = unsafe { get_device_state(evdev, &capabilities) };

        // According to the documentation, libevdev_get_name() never returns a null pointer
        // but may return an empty string. We are not sure whether the return value is guaranteed
        // to be UTF-8 decodable, so it may be possible that device_name ends up as None.
        let name: InputDeviceName = unsafe {
            CStr::from_ptr(libevdev::libevdev_get_name(evdev))
        }.to_owned();

        // Set the clock to CLOCK_MONOTONIC, which is the same clock used for all other time-
        // related operations used in evsieve. Using the monotonic clock instead of the
        // realtime clock makes sure that some event will not end up getting delayed by many
        // days in case the user decides to set their clock back.
        //
        // The libevdev documentation says that "This is a modification only affecting this
        // representation of this device."; setting this clock id should not affect other programs.
        let res = unsafe { libevdev::libevdev_set_clock_id(evdev, libc::CLOCK_MONOTONIC) };
        if res < 0 {
            eprintln!("Warning: failed to set the clock to CLOCK_MONOTONIC on the device opened from {}.\nThis is a non-fatal error, but any time-related operations such as the --delay argument will behave incorrectly.", path.to_string_lossy());
        }

        Ok(InputDevice {
            file, path, evdev, domain, capabilities, state, name,
            grab_mode: pre_device.grab_mode, grabbed: false,
            persist_mode: pre_device.persist_mode,
        })
    }

    pub fn domain(&self) -> Domain {
        self.domain
    }

    fn read_raw(&mut self) -> Result<Vec<(Instant, EventCode, EventValue)>, SystemError> {
        let mut event: MaybeUninit<libevdev::input_event> = MaybeUninit::uninit();
        let mut should_sync = false;
        let mut events: Vec<(Instant, EventCode, EventValue)> = Vec::new();

        loop {
            let flags = match should_sync {
                true => libevdev::libevdev_read_flag_LIBEVDEV_READ_FLAG_SYNC,
                false => libevdev::libevdev_read_flag_LIBEVDEV_READ_FLAG_NORMAL,
            };
            let res = unsafe {
                libevdev::libevdev_next_event(self.evdev, flags, event.as_mut_ptr())
            };

            const SUCCESS: i32 = libevdev::libevdev_read_status_LIBEVDEV_READ_STATUS_SUCCESS as i32;
            const SYNC: i32 = libevdev::libevdev_read_status_LIBEVDEV_READ_STATUS_SYNC as i32;
            const MINUS_EAGAIN: i32 = -libc::EAGAIN;
            const MINUS_EINTR: i32 = -libc::EINTR;

            match res {
                SUCCESS | SYNC => {
                    unsafe {
                        let event = event.assume_init();
                        let event_type = EventType::new(event.type_);
                        let event_code = EventCode::new(event_type, event.code);
                        let event_time = event.time.into();
                        events.push((event_time, event_code, event.value));
                    }

                    should_sync = res == SYNC;
                },
                MINUS_EAGAIN => break,
                MINUS_EINTR => break,
                _ => return Err(SystemError::new(
                    "An unknown error occured while reading from an event device."
                )),
            }
        }

        Ok(events)
    }

    /// Given an event code and value, creates an `Event` that has all entries filled
    /// out as if it was a real event that was received by this input device. Updates
    /// the state of `self` as if this event was really received. The resulting event
    /// shall be directly returned by this function; it will not be queried to be
    /// returned by `poll()`.
    ///
    /// This function is public and is callable from both this class' member functions
    /// to process real events, as well as from other parts in the code to simulate
    /// having received events.
    pub fn synthesize_event(&mut self, code: EventCode, value: EventValue) -> Event {
        let previous_value_mut: &mut EventValue = self.state.entry(code).or_insert(0);
        let previous_value: EventValue = *previous_value_mut;
        *previous_value_mut = value;
        Event::new(
            code, value, previous_value, self.domain, Namespace::Input,
        )
    }

    /// Reads the raw events from the device and attached additional information such as the
    /// domain of this device and whatever value this event had the last time it was seen.
    pub fn poll(&mut self) -> Result<Vec<(Instant, Event)>, SystemError> {
        let events: Vec<(Instant, Event)> = self.read_raw()?
            .into_iter()
            .map(|(time, code, value)| (time, self.synthesize_event(code, value)))
            .collect();

        self.grab_if_desired()?;
        Ok(events)
    }

    /// Tries to grab the device if grab_mode says we should.
    ///
    /// Returns Ok if either grabbing was successful or there is no need to grab this device.
    /// Returns Err(SystemError) if we tried to grab the device, but failed because the OS didn't
    /// let us grab the device.
    pub fn grab_if_desired(&mut self) -> Result<(), SystemError> {
        if self.grabbed {
            return Ok(());
        }
        match self.grab_mode {
            GrabMode::None => Ok(()),
            GrabMode::Force => self.grab(),
            GrabMode::Auto => {
                // Grab if no key is currently pressed.
                if self.get_pressed_keys().count() > 0 {
                    return Ok(());
                }
                self.grab()
            }
        }
    }

    /// Returns an iterator of all EV_KEY codes that are currently pressed.
    pub fn get_pressed_keys(&self) -> impl Iterator<Item=EventCode> + '_ {
        self.state.iter()
            .filter(|(code, value)| code.ev_type().is_key() && **value > 0)
            .map(|(&code, &_value)| code)
    }

    fn grab(&mut self) -> Result<(), SystemError> {
        let res = unsafe {
            libevdev::libevdev_grab(self.evdev, libevdev::libevdev_grab_mode_LIBEVDEV_GRAB)
        };
        if res < 0 {
            Err(SystemError::new(
                format!("Failed to grab input device: {}", self.path.to_string_lossy()
            )))
        } else {
            self.grabbed = true;
            Ok(())
        }
    }

    fn ungrab(&mut self) -> Result<(), SystemError> {
        let res = unsafe {
            libevdev::libevdev_grab(self.evdev, libevdev::libevdev_grab_mode_LIBEVDEV_GRAB)
        };
        if res < 0 {
            Err(SystemError::new(
                format!("Failed to ungrab event device: {}", self.path.to_string_lossy()
            )))
        } else {
            self.grabbed = false;
            Ok(())
        }
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn capabilities(&self) -> &Capabilities {
        &self.capabilities
    }

    pub fn name(&self) -> &InputDeviceName {
        &self.name
    }

    pub fn persist_mode(&self) -> PersistMode {
        self.persist_mode
    }

    // Closes the device and returns a blueprint from which it can be reopened.
    pub fn to_blueprint(self) -> Blueprint {
        Blueprint {
            capabilities: self.capabilities.clone(),
            name: self.name.clone(),
            pre_device: PreInputDevice {
                path: self.path.clone(),
                grab_mode: self.grab_mode,
                domain: self.domain,
                persist_mode: self.persist_mode,
            },
        }
    }
}

/// This implement is necessary becaus *mut libevdev::libevdev is not Send.
unsafe impl Send for InputDevice {}

/// # Safety
/// Exhibits undefined behaviour if evdev is not a valid pointer.
unsafe fn get_capabilities(evdev: *mut libevdev::libevdev) -> Capabilities {
    let event_types = ecodes::event_types();
    
    let supported_event_types = event_types.filter(|&ev_type| {
        libevdev::libevdev_has_event_type(evdev, ev_type.into()) == 1
    });

    let supported_event_codes: HashSet<EventCode> =
        supported_event_types
        .flat_map(ecodes::event_codes_for)
        .filter(|code| {
            libevdev::libevdev_has_event_code(evdev, code.ev_type().into(), code.code() as u32) == 1
        }).collect();
    
    // Query the abs_info from this device.
    let mut abs_info: HashMap<EventCode, AbsInfo> = HashMap::new();
    for &code in &supported_event_codes {
        if code.ev_type().is_abs() {
            let evdev_abs_info: *const libevdev::input_absinfo = libevdev::libevdev_get_abs_info(evdev, code.code() as u32);
            abs_info.insert(code, (*evdev_abs_info).into());
        }
    }

    // Query rep_info from this device.
    let rep_info = {
        let mut delay: libc::c_int = 0;
        let mut period: libc::c_int = 0;
        let res = libevdev::libevdev_get_repeat(evdev, &mut delay, &mut period);
        match res {
            0 => Some(RepeatInfo { delay, period }),
            _ => None,
        }
    };

    Capabilities {
        codes: supported_event_codes,
        abs_info,
        rep_info,
    }
}

/// # Safety
/// Exhibits undefined behaviour if evdev is not a valid pointer or the capabilities are invalid.
unsafe fn get_device_state(evdev: *mut libevdev::libevdev, capabilities: &Capabilities) -> HashMap<EventCode, EventValue> {
    let mut device_state: HashMap<EventCode, EventValue> = HashMap::new();
    for &code in &capabilities.codes {
        // ISSUE: ABS_MT support
        if ! ecodes::is_abs_mt(code) {
            let value: i32 = libevdev::libevdev_get_event_value(evdev, code.ev_type().into(), code.code() as u32);
            device_state.insert(code, value);
        } else {
            // The return value of libevdev_get_event_value() for ABS_MT_* is undefined. Until we
            // get proper ABS_MT support, we'll use an arbitrary placeholder value.
            let value = match capabilities.abs_info.get(&code) {
                Some(abs_info) => 
                    EventValue::checked_add(abs_info.min_value, abs_info.max_value)
                        .map(|x| x / 2).unwrap_or(0),
                None => 0,
            };
            device_state.insert(code, value);
        }
        
    }
    device_state
}

impl AsRawFd for InputDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.file.as_raw_fd()
    }
}
unsafe impl HasFixedFd for InputDevice {}

impl Drop for InputDevice {
    fn drop(&mut self) {
        if self.grabbed {
            // Even if the ungrab fails, there's nothing we can do, so we ignore a possible error.
            let _ = self.ungrab();
        }

        unsafe {
            // This does not close the file descriptor itself. That part happens when
            // self.file gets dropped.
            libevdev::libevdev_free(self.evdev);
        }
    }
}
07070100000041000081A4000000000000000000000001647DB23000001096000000000000000000000000000000000000002800000000evsieve-1.4.0~0/src/io/internal_pipe.rs// SPDX-License-Identifier: GPL-2.0-or-later

//! Implements an analogue for std::sync::mpsc, except these structs use underlying an underlying Linux
//! pipe, so they can be polled using the standard POSIX API's.
//!
//! Do not use for communication with other processes or even subprocesses.

use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::os::unix::io::{RawFd, AsRawFd};
use crate::error::SystemError;
use crate::io::fd::{OwnedFd, HasFixedFd};

pub struct Sender<T: 'static> {
    /// The file descriptor of the internal pipe. Beware: Sender<T> implements HasFixedFd.
    fd: OwnedFd,
    _phantom: PhantomData<T>,
}

impl<T: 'static> Sender<T> {
    pub fn send(&self, data: T) -> Result<(), SystemError> {
        // We use MaybeUninit (and not ManuallyDrop) to signal the compiler that the data should not
        // be considered "valid" anymore after it has been sent to the kernel, so we avoid violating
        // some aliasing rules.
        let data_size: usize = std::mem::size_of::<T>();
        assert!(data_size <= libc::PIPE_BUF);
        let data = MaybeUninit::new(data);

        loop {
            let result = unsafe { libc::write(
                self.as_raw_fd(), data.as_ptr() as *const libc::c_void, data_size
            )};
            if result < 0 {
                let error = std::io::Error::last_os_error();
                match error.kind() {
                    std::io::ErrorKind::Interrupted => continue,
                    _ => return Err(error.into()),
                }
            } else if result == data_size as isize {
                // Data successfully written.
                return Ok(());
            } else {
                // A packet was partially written. This should not be possible given O_DIRECT was set.
                return Err(SystemError::new("Partial write made to internal pipe."));
            }
        }
    }
}

impl<T: 'static> AsRawFd for Sender<T> {
    fn as_raw_fd(&self) -> RawFd {
        self.fd.as_raw_fd()
    }
}
unsafe impl<T: 'static> HasFixedFd for Sender<T> {}


pub struct Receiver<T: 'static> {
    /// The file descriptor of the internal pipe. Beware: Receiver<T> implements HasFixedFd.
    fd: OwnedFd,
    _phantom: PhantomData<T>,
}

impl<T: 'static> Receiver<T> {
    pub fn recv(&self) -> Result<T, SystemError> {
        let data_size = std::mem::size_of::<T>();
        assert!(data_size <= libc::PIPE_BUF);
        let mut data: MaybeUninit<T> = MaybeUninit::uninit();

        loop {
            let result = unsafe { libc::read(
                self.as_raw_fd(), data.as_mut_ptr() as *mut libc::c_void, data_size
            )};
            if result < 0 {
                let error = std::io::Error::last_os_error();
                match error.kind() {
                    std::io::ErrorKind::Interrupted => continue,
                    _ => return Err(error.into()),
                }
            } else if result == data_size as isize {
                // Data successfully read.
                return Ok(unsafe { data.assume_init() });
            } else {
                // A packet was partially read. This should not be possible given O_DIRECT was set.
                return Err(SystemError::new("Partial packet read from internal pipe."));
            }
        }
    }
}

impl<T: 'static> AsRawFd for Receiver<T> {
    fn as_raw_fd(&self) -> RawFd {
        self.fd.as_raw_fd()
    }
}
unsafe impl<T: 'static> HasFixedFd for Receiver<T> {}


pub fn channel<T: 'static>() -> Result<(Sender<T>, Receiver<T>), SystemError> {
    assert!(std::mem::size_of::<T>() <= libc::PIPE_BUF);
    const PIPE_FLAGS: i32 = libc::O_CLOEXEC | libc::O_DIRECT | libc::O_NONBLOCK;

    let mut pipe_fds: [RawFd; 2] = [-1; 2];
    if unsafe { libc::pipe2(&mut pipe_fds as *mut _ as *mut RawFd, PIPE_FLAGS) } < 0 {
        return Err(SystemError::os_with_context("While trying to create internal communication pipes:"));
    };

    let [read_fd, write_fd] = pipe_fds;
    let owned_read_fd  = unsafe { OwnedFd::new(read_fd) };
    let owned_write_fd = unsafe { OwnedFd::new(write_fd) };

    Ok((
        Sender   { fd: owned_write_fd, _phantom: PhantomData },
        Receiver { fd: owned_read_fd,  _phantom: PhantomData },
    ))
}
07070100000042000081A4000000000000000000000001647DB23000004D13000000000000000000000000000000000000002100000000evsieve-1.4.0~0/src/io/output.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::fmt::Error;
use std::io;
use std::fs;
use std::ffi::{CString};
use std::ptr;
use std::collections::HashMap;
use std::path::{Path};
use std::path::PathBuf;
use std::fmt::Write;
use crate::event::EventType;
use crate::bindings::libevdev;
use crate::capability::{Capability, Capabilities};
use crate::event::Event;
use crate::domain::Domain;
use crate::ecodes;
use crate::error::{InternalError, RuntimeError, SystemError, Context};
use crate::predevice::{PreOutputDevice, RepeatMode};

pub struct OutputSystem {
    pre_devices: Vec<PreOutputDevice>,
    devices: HashMap<Domain, OutputDevice>,
}

impl OutputSystem {
    pub fn create(
            pre_devices: Vec<PreOutputDevice>,
            capabilities: Vec<Capability>
    ) -> Result<OutputSystem, RuntimeError> {
        // Sort the capabilities based on domain.
        let mut capability_map = capabilites_by_device(&capabilities, &pre_devices);

        // Create domains with capabilities.
        let mut devices: HashMap<Domain, OutputDevice> = HashMap::new();
        for pre_device in &pre_devices {
            let domain = pre_device.domain;

            if devices.contains_key(&domain) {
                return Err(InternalError::new("Multiple output devices with the same domain have been created.").into());
            }
    
            let capabilities = capability_map.remove(&pre_device.domain).expect("Internal invariant violated: capabilites_by_device() did not create a capability entry for each output device.");
            if capabilities.has_no_content() {
                eprintln!("Warning: an output device has been specified to which no events can possibly be routed.");
            }

            let device = create_output_device(pre_device, capabilities)?;
            
            devices.insert(domain, device);
        }

        Ok(OutputSystem { pre_devices, devices })
    }

    /// Tries to make sure that all output devices have at least the given capabilities. The output 
    /// devices may or may not end up with more capabilities than specified.
    ///
    /// This may cause output devices to be destroyed and recreated.
    pub fn update_caps(&mut self, new_capabilities: Vec<Capability>) {
        // Sort the capabilities based on domain.
        let mut capability_map = capabilites_by_device(&new_capabilities, &self.pre_devices);

        let old_output_devices = std::mem::take(&mut self.devices);
        let mut recreated_output_devices: Vec<&PreOutputDevice> = Vec::new();

        for (domain, mut old_device) in old_output_devices {
            // Find the new capabilities for this domain.
            let capabilities = match capability_map.remove(&domain) {
                Some(caps) => caps,
                None => {
                    eprintln!("Internal invariant violated: capability_map does not contain capabilities for all output devices. This is a bug.");
                    self.devices.insert(domain, old_device);
                    continue;
                },
            };

            // Find the pre_output_device with the same domain.
            let pre_device: &PreOutputDevice = match self.pre_devices.iter().find(
                |pre_device| pre_device.domain == domain
            ) {
                Some(pre_device) => pre_device,
                None => {
                    eprintln!("Internal invariant violated: OutputDeviceSystem contains an output device with a domain for which it does not have a PreOutputDevice. This is a bug.");
                    self.devices.insert(domain, old_device);
                    continue;
                },
            };

            if capabilities.is_compatible_with(&old_device.capabilities) {
                self.devices.insert(domain, old_device);
                continue;
            }

            // The device is supposed to have more capabilities than it used to. We must recreate it.
            // Free up the old symlink so the new device can create a symlink in its place.
            let symlink = old_device.take_symlink();
            drop(symlink); // TODO: MEDIUM-PRIORITY: make this operation atomical with its recreation.

            let new_device = match create_output_device(pre_device, capabilities) {
                Ok(device) => device,
                Err(error) => {
                    eprintln!("Error: failed to recreate an output device. The remaining output devices may have incorrect capabilities.");
                    error.print_err();
                    // Try to restore the old link if possible.
                    if let Some(ref path) = pre_device.create_link {
                        old_device.set_link(path.clone()).print_err();
                    }
                    self.devices.insert(domain, old_device);
                    continue;
                }
            };

            old_device.syn_if_required();
            drop(old_device);

            self.devices.insert(domain, new_device);
            recreated_output_devices.push(pre_device);
        }

        if ! recreated_output_devices.is_empty() {
            if let Ok(warning_msg) = format_output_device_recreation_warning(&recreated_output_devices) {
                println!("{}", warning_msg);
            } else {
                println!("Warning: output devices have been recreated.");
                eprintln!("Internal error: an unknown error occured in our error formatting logic. This is a bug.");
            }
        }
    }

    /// Writes all events to their respective output devices.
    pub fn route_events(&mut self, events: &[Event]) {
        for &event in events {
            let device_opt = self.devices.get_mut(&event.domain);
            match device_opt {
                Some(device) => device.write_event(event),
                None => eprintln!("Internal error: an event {} with unknown domain has been routed to output; event dropped. This is a bug.", event),
            };
        }
    }

    /// The maps may generate events without folling them up with SYN events.
    /// This function generates all SYN events for user convenience.
    pub fn synchronize(&mut self) {
        for device in self.devices.values_mut() {
            device.syn_if_required();
        }
    }
}

pub struct OutputDevice {
    device: *mut libevdev::libevdev_uinput,
    /// Keeps track of whether we've sent any events to the output since the last SYN event.
    should_syn: bool,
    /// If some symlink to the device was created, store it here.
    symlink: Option<Symlink>,
    /// If false, all repeat events sent to this device will be dropped.
    /// Does not prevent the kernel from generating repeat events.
    allows_repeat: bool,
    /// The capabilities of this output device.
    capabilities: Capabilities,
}

impl OutputDevice {
    pub fn with_name_and_capabilities(name_str: String, caps: Capabilities) -> Result<OutputDevice, RuntimeError> {
        unsafe {
            let dev = libevdev::libevdev_new();

            let cstr = CString::new(name_str).unwrap();
            let bytes = cstr.as_bytes_with_nul();
            let ptr = bytes.as_ptr();
            let name = ptr as *const libc::c_char;

            libevdev::libevdev_set_name(dev, name);

            // If EV_MSC events are automatically generated, we may need to manually activate
            // their capabilities.
            // TODO: FEATURE(auto-scan) prevent EV_MSC capabilities from getting activated by
            // capabilities.
            // ... in fact, refactor this to cooperate with the capability interface.
            if cfg!(feature = "auto-scan") {
                libevdev::libevdev_enable_event_type(dev, EventType::MSC.into());
                libevdev::libevdev_enable_event_code(
                    dev,
                    EventType::MSC.into(),
                    crate::event::EventCode::MSC_SCAN.code() as u32,
                    ptr::null_mut()
                );
            }

            for &ev_type in &caps.ev_types() {
                libevdev::libevdev_enable_event_type(dev, ev_type.into());
            }
            for code in &caps.codes {
                let res = match code.ev_type() {
                    EventType::ABS => {
                        let abs_info = caps.abs_info.get(code)
                            .ok_or_else(|| InternalError::new("Cannot create uinput device: device has absolute axis without associated capabilities."))?;
                        let libevdev_abs_info: libevdev::input_absinfo = (*abs_info).into();
                        let libevdev_abs_info_ptr = &libevdev_abs_info as *const libevdev::input_absinfo;
                        libevdev::libevdev_enable_event_code(
                            dev, code.ev_type().into(), code.code() as u32, libevdev_abs_info_ptr as *const libc::c_void)
                    },
                    EventType::REP => {
                        // Known issue: due to limitations in the uinput kernel module, the REP_DELAY
                        // and REP_PERIOD values are ignored and the kernel defaults will be used instead,
                        // according to the libevdev documentation. Status: won't fix.
                        if let Some(rep_info) = caps.rep_info {
                            let value: libc::c_int = match code.code() {
                                ecodes::REP_DELAY => rep_info.delay,
                                ecodes::REP_PERIOD => rep_info.period,
                                _ => {
                                    eprintln!("Warning: encountered an unknown capability code under EV_REP: {}.", code.code());
                                    continue;
                                },
                            };
                            libevdev::libevdev_enable_event_code(dev, code.ev_type().into(), code.code() as u32, &value as *const libc::c_int as *const libc::c_void)
                        } else {
                            eprintln!("Internal error: an output device claims EV_REP capabilities, but no repeat info is available.");
                            continue;
                        }
                    },
                    _ => libevdev::libevdev_enable_event_code(dev, code.ev_type().into(), code.code() as u32, ptr::null_mut()),
                };
                if res < 0 {
                    eprintln!("Warning: failed to enable event {} on uinput device.", ecodes::event_name(*code));
                }
            }

            let mut uinput_dev: *mut libevdev::libevdev_uinput = ptr::null_mut();
            let res = libevdev::libevdev_uinput_create_from_device(
                dev,
                // In the source code of the current version of libevdev, the O_CLOEXEC will be
                // automatically set on the created file descriptor.
                libevdev::libevdev_uinput_open_mode_LIBEVDEV_UINPUT_OPEN_MANAGED,
                &mut uinput_dev
            );

            // After we've created an UInput device based on this, we no longer need the original prototype.
            libevdev::libevdev_free(dev);

            if res != 0 {
                return Err(SystemError::new("Failed to create an UInput device. Does evsieve have enough permissions?").into());
            }

            Ok(OutputDevice {
                device: uinput_dev,
                should_syn: false,
                symlink: None,
                allows_repeat: true,
                capabilities: caps,
            })
        }
    }

    fn write(&mut self, ev_type: u32, code: u32, value: i32) {
        if ! self.allows_repeat && ev_type == ecodes::EV_KEY.into() && value == 2 {
            return;
        }
        let res = unsafe { libevdev::libevdev_uinput_write_event(self.device, ev_type, code, value) };
        if res < 0 {
            eprintln!("Warning: an error occurred while writing an event to {}.", self.description());
        }
        self.should_syn = ev_type != libevdev::EV_SYN;
    }

    #[cfg(not(feature = "auto-scan"))]
    fn write_event(&mut self, event: Event) {
        self.write(event.code.ev_type().into(), event.code.code() as u32, event.value);
    }

    #[cfg(feature = "auto-scan")]
    fn write_event(&mut self, event: Event) {
        // TODO: LOW-PRIORITY conside moving the following snippet to another stage of the event pipeline.
        if event.ev_type() == EventType::KEY && (event.value == 0 || event.value == 1) {
            if let Some(scancode) = crate::scancodes::from_event_code(event.code) {
                self.write(EventType::MSC.into(), crate::event::EventCode::MSC_SCAN.code().into(), scancode)
            }
        }
        self.write(event.code.ev_type().into(), event.code.code() as u32, event.value as i32);
    }

    fn syn_if_required(&mut self) {
        if self.should_syn {
            self.write(libevdev::EV_SYN, 0, 0);
        }
    }

    /// Returns a handy name for this device, useful for error logging.main
    fn description(&self) -> String {
        if let Some(link) = &self.symlink {
            format!("the output device \"{}\"", link.location().to_string_lossy())
        } else {
            "an output device".to_string()
        }
    }

    fn set_link(&mut self, path: PathBuf) -> Result<(), SystemError> {
        // Try to figure out the path of the uinput device node.
        let my_path_cstr_ptr = unsafe {
            libevdev::libevdev_uinput_get_devnode(self.device)
        };
        if my_path_cstr_ptr.is_null() {
            return Err(SystemError::new("Failed to createa a symlink to an output device: cannot determine the path to the virtual device's device node."))
        };
        let my_path_cstr = unsafe { std::ffi::CStr::from_ptr(my_path_cstr_ptr) };
        let my_path_str = my_path_cstr.to_str().map_err(|_|
            SystemError::new("Failed to createa a symlink to an output device: the path to the virtual device node is not valid UTF-8.")
        )?;
        let my_path = Path::new(my_path_str).to_owned();

        // Drop the old link before creating a new one, in case the old and new link are both at the
        // same location.
        drop(self.take_symlink());
        self.symlink = Some(Symlink::create(my_path, path)?);
        Ok(())
    }

    /// Decouples this device from the symlink pointing to it.
    fn take_symlink(&mut self) -> Option<Symlink> {
        self.symlink.take()
    }

    fn set_repeat_mode(&mut self, mode: RepeatMode) {
        self.allow_repeat(match mode {
            RepeatMode::Passive  => true,
            RepeatMode::Disable  => false,
            RepeatMode::Enable   => false,
        });
    }

    fn allow_repeat(&mut self, value: bool) {
        self.allows_repeat = value;
    }
}

impl Drop for OutputDevice {
    fn drop(&mut self) {
        unsafe {
            libevdev::libevdev_uinput_destroy(self.device);
        }
    }
}

/// Represents a symlink on the filesystem. Has RAII support.
struct Symlink {
    /// Where the symlink points to.
    _source: PathBuf,
    /// Where the actual symlink lives.
    location: PathBuf,
}

impl Symlink {
    /// Creates a symlink at dest that points to source.
    fn create(source: PathBuf, dest: PathBuf) -> Result<Symlink, io::Error> {
        // Handle already existing files: overwrite if it exists and is a symlink,
        // otherwise leave untouched. This behaviour has been chosen as the optimal
        // balance between being nondestructive yet also not confusing the user with
        // errors after re-running a script that has been SIGKILL'd.
        if let Ok(metadata) = fs::symlink_metadata(&dest) {
            if metadata.file_type().is_symlink() {
                fs::remove_file(&dest)?;
            } else {
                return Err(io::Error::new(io::ErrorKind::AlreadyExists,
                    format!("Cannot create a symlink at \"{}\": path already exists.", dest.to_string_lossy())));
            }
        }

        std::os::unix::fs::symlink(&source, &dest)?;
        Ok(Symlink {
            _source: source, location: dest
        })
    }

    fn location(&self) -> &Path {
        &self.location
    }
}

impl Drop for Symlink {
    fn drop(&mut self) {
        let _ = fs::remove_file(&self.location);
    }
}

/// Sorts a vector of capabilities by domain and adjusts them based on explicit flags/clauses specified
/// for the output devices. Guarantees that an entry exists for each pre-output device.
fn capabilites_by_device(capabilities: &[Capability], pre_devices: &[PreOutputDevice])
        -> HashMap<Domain, Capabilities>
{
    let mut capability_map: HashMap<Domain, Capabilities> = HashMap::new();
    for capability in capabilities.iter().copied() {
        let domain_capabilities = capability_map.entry(capability.domain).or_insert_with(Capabilities::new);
        domain_capabilities.add_capability(capability);
    }

    for device in pre_devices {
        let device_caps = capability_map.entry(device.domain).or_insert_with(Capabilities::new);
        match device.repeat_mode {
            RepeatMode::Disable => device_caps.remove_ev_rep(),
            RepeatMode::Passive => device_caps.remove_ev_rep(),
            RepeatMode::Enable  => device_caps.require_ev_rep(),
        };
    }

    capability_map
}

fn create_output_device(pre_device: &PreOutputDevice, capabilities: Capabilities) -> Result<OutputDevice, RuntimeError> {
    let mut device = OutputDevice::with_name_and_capabilities(pre_device.name.clone(), capabilities)
        .with_context(match pre_device.create_link.clone() {
            Some(path) => format!("While creating the output device \"{}\":", path.display()),
            None => "While creating an output device:".to_string(),
        })?;

    device.set_repeat_mode(pre_device.repeat_mode);

    if let Some(ref path) = pre_device.create_link {
        device.set_link(path.clone())
            .map_err(SystemError::from)
            .with_context(format!("While creating a symlink at \"{}\":", path.display()))?;
    };

    Ok(device)
}

fn format_output_device_recreation_warning(recreated_devices: &[&PreOutputDevice]) -> Result<String, Error>  {
    if recreated_devices.is_empty() {
        return Ok("".to_owned());
    }
    let named_recreated_devices: Vec<String> = recreated_devices.iter().filter_map(
        |device| device.create_link.as_ref().map(
            |path| format!("\"{}\"", path.display())
        )
    ).collect();
    let num_unnamed_recreated_devices = recreated_devices.len() - named_recreated_devices.len();

    let mut msg: String = "Warning: due to a change in the capabilities of the input devices, ".to_string();
    if named_recreated_devices.is_empty() {
        match num_unnamed_recreated_devices {
            1 => write!(&mut msg, "an output device ")?,
            n => write!(&mut msg, "{} output devices ", n)?,
        };
    } else {
        match named_recreated_devices.len() {
            1 => write!(&mut msg, "the output device ")?,
            _ => write!(&mut msg, "the output devices ")?,
        };
        write!(&mut msg, "{}", named_recreated_devices.join(", "))?;
        match num_unnamed_recreated_devices {
            0 => write!(&mut msg, " ")?,
            1 => write!(&mut msg, ", and an unnamed output device ")?,
            n => write!(&mut msg, ", and {} unnamed output devices ", n)?,
        };
    };

    match recreated_devices.len() {
        1 => write!(&mut msg, "has been destroyed and recreated. ")?,
        _ => write!(&mut msg, "have been destroyed and recreated. ")?,
    }
    write!(&mut msg, "This may cause other programs that have grabbed the output devices to lose track of them.")?;

    Ok(msg)
}07070100000043000081A4000000000000000000000001647DB23000007023000000000000000000000000000000000000001B00000000evsieve-1.4.0~0/src/key.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::affine::AffineFactor;
use crate::domain;
use crate::domain::Domain;
use crate::event::{Event, EventType, EventCode, Channel, Namespace, VirtualEventType};
use crate::utils;
use crate::error::ArgumentError;
use crate::capability::{Capability, CapMatch};
use crate::range::Range;
use crate::ecodes;
use crate::error::Context;

#[derive(Clone, Debug)]
pub struct Key {
    /// Upholds invariant: at most one copy of each KeyProperty variant may be in this vector.
    /// Putting multiple copies in it is a logical error; some functions may not function
    /// correctly if this invariant is broken.
    properties: Vec<KeyProperty>,
}

impl Key {
    fn new() -> Key {
        Key {
            properties: Vec::new()
        }
    }

    /// Generates a key with no properties. Identical to Key::new(), but semantically signifies
    /// that you intend to use this key to generate identical copies of events, such as for the
    /// purpose of implementing --copy. You should not use this function if you intend to add more
    /// properties to a key.
    pub fn copy() -> Key {
        Key::new()
    }

    /// Returns a key that matches all events with a certain domain and namespace.
    pub fn from_domain_and_namespace(domain: Domain, namespace: Namespace) -> Key {
        let mut result = Key::new();
        result.add_property(KeyProperty::Namespace(namespace));
        result.add_property(KeyProperty::Domain(domain));
        result
    }

    pub fn matches(&self, event: &Event) -> bool {
        self.properties.iter().all(|prop| prop.matches(event))
    }

    /// Returns true if this Key might match any Event with a given channel.
    pub fn matches_channel(&self, channel: Channel) -> bool {
        self.properties.iter().all(|prop| prop.matches_channel(channel))
    }

    /// Returns Yes if this key will guaranteedly match any event said Capability might emit,
    /// returns No if it cannot possibly match any such event, and otherwise Maybe.
    pub fn matches_cap(&self, cap: &Capability) -> CapMatch {
        self.properties.iter().map(|prop| prop.matches_cap(cap)).min().unwrap_or(CapMatch::Yes)
    }

    pub fn merge(&self, mut event: Event) -> Event {
        for prop in &self.properties {
            event = prop.merge(event);
        }
        event
    }

    pub fn merge_cap(&self, mut cap: Capability) -> Capability {
        for prop in &self.properties {
            cap = prop.merge_cap(cap);
        }
        cap
    }

    /// If this key has a `Value` property, returns the range of said property and removes
    /// it from its own properties. This is useful to decouple the key being matched upon
    /// from the value being matched upon.
    /// 
    /// The result is undefined if this key has multiple copies of a `Value` property.
    pub fn pop_value(&mut self) -> Option<Range> {
        let mut result: Option<Range> = None;
        self.properties.retain(
            |&property| {
                match property {
                    KeyProperty::Value(range) => {
                        result = Some(range);
                        false
                    },
                    _ => true,
                }
            }
        );
        result
    }

    fn add_property(&mut self, property: KeyProperty) {
        self.properties.push(property);
    }

    /// Returns Some(EventType) if this Key will only ever accept events of a certain type.
    /// If it may accept different types of events, returns None.
    pub fn requires_event_type(&self) -> Option<EventType> {
        for property in &self.properties {
            match property {
                KeyProperty::Code(code) => return Some(code.ev_type()),
                KeyProperty::Type(ev_type) => return Some(*ev_type),
                KeyProperty::VirtualType(v_type) => return Some(v_type.ev_type()),
                KeyProperty::Domain(_)
                | KeyProperty::Namespace(_)
                | KeyProperty::Value(_)
                | KeyProperty::PreviousValue(_)
                | KeyProperty::AffineFactor(_)
                => (),
            }
        }
        None
    }

    /// Returns true if some event may match both key_1 and key_2.
    pub fn intersects_with(&self, other: &Key) -> bool {
        // Tests interaction between (Type, VirtualType) and (Type, Code).
        if let (Some(ev_type_1), Some(ev_type_2)) = (self.requires_event_type(), other.requires_event_type()) {
            if ev_type_1 != ev_type_2 {
                return false;
            }
        }

        for prop_1 in &self.properties {
            for prop_2 in &other.properties {
                let these_properties_may_intersect = match (prop_1, prop_2) {
                    (KeyProperty::Code(left), KeyProperty::Code(right))
                        => left == right,
                    (KeyProperty::Domain(left), KeyProperty::Domain(right))
                        => left == right,
                    (KeyProperty::Namespace(left), KeyProperty::Namespace(right))
                        => left == right,

                    (KeyProperty::VirtualType(left), KeyProperty::VirtualType(right))
                        => left == right,
                    (KeyProperty::VirtualType(v_type), KeyProperty::Code(code))
                    | (KeyProperty::Code(code), KeyProperty::VirtualType(v_type))
                        => *v_type == code.virtual_ev_type(),

                    (KeyProperty::Value(left), KeyProperty::Value(right))
                    | (KeyProperty::PreviousValue(left), KeyProperty::PreviousValue(right))
                        => left.intersects_with(right),
                    
                    (KeyProperty::Code(_), _)
                    | (KeyProperty::Domain(_), _)
                    | (KeyProperty::Namespace(_), _)
                    | (KeyProperty::Type(_), _)
                    | (KeyProperty::VirtualType(_), _)
                    | (KeyProperty::Value(_), _)
                    | (KeyProperty::PreviousValue(_), _)
                    | (KeyProperty::AffineFactor(_), _)
                        => true,
                };
                if ! these_properties_may_intersect {
                    return false;
                }
            }
        }

        true
    }
}

#[derive(Clone, Copy, Debug)]
enum KeyProperty {
    Code(EventCode),
    Domain(Domain),
    Namespace(Namespace),
    Value(Range),
    PreviousValue(Range),
    /// Only valid for filter keys.
    Type(EventType),
    /// Only valid for filter keys.
    VirtualType(VirtualEventType),
    /// Applies an affine transformation on the input event.
    /// Only valid for mask keys.
    AffineFactor(AffineFactor),
}

impl KeyProperty {
    /// Checkes whether an event matches this KeyProperty.
    pub fn matches(&self, event: &Event) -> bool {
        match *self {
            KeyProperty::Code(value) => event.code == value,
            KeyProperty::Domain(value) => event.domain == value,
            KeyProperty::Type(value) => event.code.ev_type() == value,
            KeyProperty::VirtualType(value) => event.code.virtual_ev_type() == value,
            KeyProperty::Namespace(value) => event.namespace == value,
            KeyProperty::Value(range) => range.contains(event.value),
            KeyProperty::PreviousValue(range) => range.contains(event.previous_value),
            KeyProperty::AffineFactor(_) => {
                // Similarly to `KeyProperty::merge`, benchmarks show that the mere threat of panicking
                // during this function can significantly reduce performance, therefore this assertion
                // is only made during debug builds.
                if cfg!(debug_assertions) {
                    panic!("Cannot filter events based on relative values. Panicked during event mapping.");
                }
                false
            },
        }
    }

    /// Checks whether this Keyproperty might match any event with a given channel.
    pub fn matches_channel(&self, channel: Channel) -> bool {
        let (code, domain) = channel;
        match *self {
            KeyProperty::Code(value) => code == value,
            KeyProperty::Domain(value) => domain == value,
            KeyProperty::Type(value) => value == code.ev_type(),
            KeyProperty::VirtualType(value) => value.ev_type() == code.ev_type(),
            KeyProperty::Namespace(_)
            | KeyProperty::Value(_)
            | KeyProperty::PreviousValue(_)
            | KeyProperty::AffineFactor(_)
                => true,
        }
    }

    /// Given an Event, will return the closest event that matches this KeyProperty.
    pub fn merge(&self, mut event: Event) -> Event {
        match *self {
            KeyProperty::Code(value) => event.code = value,
            KeyProperty::Domain(value) => event.domain = value,
            KeyProperty::Namespace(value) => event.namespace = value,
            KeyProperty::Value(range) => event.value = range.bound(event.value),
            KeyProperty::PreviousValue(range) => event.previous_value = range.bound(event.previous_value),
            KeyProperty::AffineFactor(factor) => {
                event = factor.merge(event);
            },
            KeyProperty::Type(_) | KeyProperty::VirtualType(_) => {
                if cfg!(debug_assertions) {
                    panic!("Cannot change the event type of an event. Panicked during event mapping.");
                } else {
                    // Do nothing.
                    //
                    // I would like to print a warning here, but benchmarks show that makes evsieve's
                    // performance 33% slower, just for a situation that should both never happen and
                    // if it did happen, would most likely be caught during capability propagation.
                    //
                    // utils::warn_once("Internal error: cannot change the event type of an event. If you see this message, this is a bug.");
                }
            }
        };
        event
    }

    pub fn matches_cap(&self, cap: &Capability) -> CapMatch {
        match *self {
            KeyProperty::Code(value) => (cap.code == value).into(),
            KeyProperty::Domain(value) => (cap.domain == value).into(),
            KeyProperty::Type(value) => (cap.code.ev_type() == value).into(),
            KeyProperty::VirtualType(value) => (cap.code.virtual_ev_type() == value).into(),
            KeyProperty::Namespace(value) => (cap.namespace == value).into(),
            KeyProperty::Value(range) => {
                if cap.value_range.is_subset_of(&range) {
                    CapMatch::Yes
                } else if range.is_disjoint_with(&cap.value_range) {
                    CapMatch::No
                } else {
                    CapMatch::Maybe
                }
            },
            KeyProperty::PreviousValue(_range) => CapMatch::Maybe,
            KeyProperty::AffineFactor(_) => {
                panic!("Internal invariant violated: cannot filter events based on relative values.");
            },
        }
    }

    pub fn merge_cap(&self, mut cap: Capability) -> Capability {
        match *self {
            KeyProperty::Code(value) => cap.code = value,
            KeyProperty::Domain(value) => cap.domain = value,
            KeyProperty::Namespace(value) => cap.namespace = value,
            KeyProperty::Value(range) => cap.value_range = range.bound_range(&cap.value_range),
            KeyProperty::PreviousValue(_range) => {},
            KeyProperty::AffineFactor(factor) => cap = factor.merge_cap(cap),
            KeyProperty::Type(_) | KeyProperty::VirtualType(_) => {
                if cfg!(debug_assertions) {
                    panic!("Cannot change the event type of an event. Panicked during capability propagation.");
                } else {
                    utils::warn_once("Internal error: cannot change the event type of an event. If you see this message, this is a bug.");
                }
            },
        };
        cap
    }
}

/// Represents the options for how a key can be parsed in different contexts.
#[allow(non_snake_case)]
pub struct KeyParser<'a> {
    pub default_value: &'a str,
    /// Whether event values like the :1 in "key:a:1" are allowed.
    pub allow_values: bool,
    pub allow_transitions: bool,
    pub allow_ranges: bool,
    /// Whether keys with only a type like "key", "btn", "abs", and such without an event code, are allowed.
    /// Only ever set this to true for filter keys.
    pub allow_types: bool,
    /// Whether keys with an event value that depends on which event is getting masked, are allowed.
    /// Only ever set this to true for mask keys.
    pub allow_relative_values: bool,
    /// Allows the empty key "" and all keys starting with "btn" or "key". Forbids keys that
    /// explicitly require another type.
    pub forbid_non_EV_KEY: bool,

    pub namespace: Namespace,
}

impl<'a> KeyParser<'a> {
    /// Returns the KeyParser that is the most commonly used one for filtering events,
    /// e.g. the first key in `--map key:a key:b` but not the second key.
    pub fn default_filter() -> KeyParser<'static> {
        KeyParser {
            default_value: "",
            allow_values: true,
            allow_ranges: true,
            allow_transitions: true,
            allow_types: true,
            allow_relative_values: false,
            forbid_non_EV_KEY: false,
            namespace: Namespace::User,
        }
    }

    /// Returns a KeyParser that functions the same as self, except that it will only
    /// parse keys that would be deemed to be valid keys according to `other` as well.
    /// 
    /// Does not guarantee that `self` and `other` would parse keys to the same key.
    pub fn and_filter(self, other: KeyParser) -> Self {
        KeyParser {
            default_value: self.default_value,
            allow_values: self.allow_values && other.allow_values,
            allow_transitions: self.allow_transitions && other.allow_transitions,
            allow_ranges: self.allow_ranges && other.allow_ranges,
            allow_types: self.allow_types && other.allow_types,
            allow_relative_values: self.allow_relative_values && other.allow_relative_values,
            forbid_non_EV_KEY: self.forbid_non_EV_KEY || other.forbid_non_EV_KEY,
            namespace: self.namespace,
        }
    }

    /// Returns the KeyParser that is the most commonly used one for masking events,
    /// e.g. the second key in `--map key:a key:b` but not the first key.
    pub fn default_mask() -> KeyParser<'static> {
        KeyParser {
            default_value: "",
            allow_values: true,
            allow_ranges: false,
            allow_transitions: false,
            allow_types: false,
            allow_relative_values: true,
            forbid_non_EV_KEY: false,
            namespace: Namespace::User,
        }
    }

    /// Returns a Keyparser that only interprets "pure" keys, i.e. keys for which
    /// `key.matches(event) == key.matches_channel(event.channel)` is true for all events.
    pub fn pure() -> KeyParser<'static> {
        KeyParser {
            default_value: "",
            allow_values: false,
            allow_ranges: false,
            allow_transitions: false,
            allow_types: true,
            allow_relative_values: false,
            forbid_non_EV_KEY: false,
            namespace: Namespace::User,
        }
    }

    pub fn with_namespace(&mut self, namespace: Namespace) -> &mut Self {
        self.namespace = namespace;
        self
    }

    pub fn parse(&self, key_str: &str) -> Result<Key, ArgumentError> {
        interpret_key_with_domain(key_str, self)
            .with_context(format!("While parsing the key \"{}\":", key_str))
    }

    pub fn parse_all(&self, key_strs: &[String]) -> Result<Vec<Key>, ArgumentError> {
        key_strs.iter().map(
            |key| self.parse(key)
        ).collect()
    }
}


/// Tells you whether the string looks like a key and should be interpreted as such if it is
/// passed to an argument. Intentionally does not guarantee that it will actually parse as key,
/// so sensible error messages can be given when the user enters a somewhat incorrect key and
/// be told why it isn't a key. If this function worked perfectly, such keys might get seen as
/// flags and the user might end up with useless error messages such as "the --map argument
/// doesn't take a key:lctrl flag".
pub fn resembles_key(key_str: &str) -> bool {
    // Make sure we don't confuse a path for a key.
    if key_str.starts_with('/') {
        false
    } else {
        let before_equals_part = utils::split_once(key_str, "=").0;

        // Check if it is an actual key.
        KeyParser {
            default_value: "",
            allow_values: true,
            allow_ranges: true,
            allow_transitions: true,
            allow_types: true,
            allow_relative_values: true,
            forbid_non_EV_KEY: false,
            namespace: Namespace::User,
        }.parse(key_str).is_ok()
        // Otherwise, check if it contains some of the key-like characters.
        // No flag or clause name should contain a : or @ to make sure they're not mistaken for keys.
        || before_equals_part.contains(':')
        || before_equals_part.contains('@')
        || before_equals_part.contains('%')
    }
}

/// Interprets a key that optionally has a domain attached, like "key:a@keyboard".
fn interpret_key_with_domain(key_str: &str, parser: &KeyParser) -> Result<Key, ArgumentError> {
    let parts = key_str_to_parts(key_str)?;
    let mut key = interpret_key(parts, parser)?;

    if let Some(domain_str) = parts.domain {
        let domain = domain::resolve(domain_str)?;
        key.properties.push(KeyProperty::Domain(domain));
    }

    Ok(key)
}

#[derive(Clone, Copy)]
struct KeyParts<'a> {
    /// The full string of which these parts were lexed.
    key_str: &'a str,

    // The following three fields represent respectively the "key", "a" and "1" parts of a string
    // like "key:a:1". They will never equal Some(""); in those cases they should be turned into
    // None instead.
    ev_type: Option<&'a str>,
    code: Option<&'a str>,
    value: Option<&'a str>,

    domain: Option<&'a str>,
}

fn key_str_to_parts(key_str: &str) -> Result<KeyParts, ArgumentError> {
    let (key_str, domain) = utils::split_once(key_str, "@");

    let mut parts_iter = key_str.split(':').peekable();

    // Make sure that we never store the empty string in the type, code, or value options.
    fn treat_empty_as_none(option: Option<&str>) -> Option<&str> {
        option.filter(|content| !content.is_empty())
    }
    let ev_type = treat_empty_as_none(parts_iter.next());
    let code = treat_empty_as_none(parts_iter.next());
    let value = treat_empty_as_none(parts_iter.next());

    // This forbids keys like "key:", "key:a:" or "key::".
    if key_str.ends_with(':') {
        return Err(ArgumentError::new(
            format!(
                "A key must not end on a colon (\":\"). Please try \"{}\" instead.",
                key_str.trim_end_matches(':')
            )
        ));
    }

    // Make sure there is nothing after the last colon, such as in key:a:1:2.
    // TODO: TEST THIS
    if parts_iter.peek().is_some() {
        let superfluous_part = parts_iter.collect::<Vec<_>>().join(":");
        return Err(ArgumentError::new(format!(
            "Too many colons encountered in the key \"{}\". There is no way to interpret the \":{}\" part.", key_str, superfluous_part
        )));
    }

    Ok(KeyParts {
        key_str,
        ev_type,
        code,
        value,
        domain,
    })
}

fn interpret_key(parts: KeyParts, parser: &KeyParser) -> Result<Key, ArgumentError> {
    let mut key = Key::new();
    key.add_property(KeyProperty::Namespace(parser.namespace));

    if parts.code.is_some() && parts.ev_type.is_none() {
        // TODO: LOW-PRIORITY: Consider allowing this instead of throwing an error.
        return Err(ArgumentError::new("Cannot specify event code or value without specifying event type."));
    }

    // Interpret the event type.
    if let Some(event_type_name) = parts.ev_type {
        let event_type = ecodes::event_type(event_type_name)?;

        if event_type.is_syn() {
            return Err(ArgumentError::new("Cannot use event type \"syn\": it is impossible to manipulate synchronisation events because synchronisation is automatically taken care of by evsieve."));
        }
        if parser.forbid_non_EV_KEY && event_type != EventType::KEY {
            return Err(ArgumentError::new(
                "Only events of type EV_KEY (i.e. \"key:something\" or \"btn:something\") can be specified in this position."
            ))
        }

        // Extract the event code, or set a property that matches on type only.
        match parts.code {
            // If no event code is available, then either throw an error or return a key that matches only on
            // the virtual type depending on whether parser.allow_types is set.
            //
            // The Some("") case should never happen, but we add this match for defensive programming.
            None | Some("") => {
                if ! parser.allow_types {
                    return Err(ArgumentError::new(format!("No event code provided for the key \"{}\".", parts.key_str)));
                }

                let property = match event_type_name {
                    VirtualEventType::KEY => KeyProperty::VirtualType(VirtualEventType::Key),
                    VirtualEventType::BUTTON => KeyProperty::VirtualType(VirtualEventType::Button),
                    _ => KeyProperty::Type(event_type),
                };
                key.add_property(property);
            },
            Some(event_code_name) => {
                let event_code = ecodes::event_code(event_type_name, event_code_name)?;
                key.add_property(KeyProperty::Code(event_code));

                // ISSUE: ABS_MT support
                if ecodes::is_abs_mt(event_code) {
                    utils::warn_once("Warning: it seems you're trying to manipulate ABS_MT events. Keep in mind that evsieve's support for ABS_MT is considered unstable. Evsieve's behaviour with respect to ABS_MT events is subject to change in the future.");
                }
            }
        };
    } 

    let event_value_str = match parts.value {
        Some(value) => {
            if parser.allow_values {
                value
            } else {
                return Err(ArgumentError::new(format!(
                    "This argument does not allow you to specify values for its events. Try removing the \":{}\" part.",
                    value
                )))
            }
        },
        None => match parser.default_value {
            "" => return Ok(key),
            _ => parser.default_value,
        },
    };

    // Check if it is a relative value.
    match interpret_relative_value(event_value_str) {
        AffineParseResult::IsAffine(property) => {
            if parser.allow_relative_values {
                key.add_property(property);
                return Ok(key);
            } else {
                return Err(ArgumentError::new(format!(
                    "It is not possible to specify relative values for the key {}.", parts.key_str,
                )))
            }
        },
        AffineParseResult::IsConstant(property) => {
            if parser.allow_relative_values {
                key.add_property(property);
                return Ok(key);
            } else {
                // Do nothing.
                //
                // We do not want to accept it yet because that could lead to cases like
                // ::4+0x getting accepted where it shouldn't, but we shouldn't throw an
                // error either because that could cause errors on ::4 getting interpreted
                // as an affine factor;
            }
        },
        AffineParseResult::Unparsable => (),
    }

    // Determine what the previous event value (if any) is, and the current event value.
    let (val_1, val_2) = utils::split_once(event_value_str, "..");
    let (previous_value_str_opt, current_value_str) = match val_2 {
        Some(val) => (Some(val_1), val),
        None => (None, val_1),
    };

    let current_value = interpret_event_value(current_value_str, parser)?;
    key.add_property(KeyProperty::Value(current_value));

    if let Some(previous_value_str) = previous_value_str_opt {
        if ! parser.allow_transitions {
            return Err(ArgumentError::new(
                "No transitions are allowed for keys in this position."
            ));
        }

        let previous_value = interpret_event_value(previous_value_str, parser)?;
        key.add_property(KeyProperty::PreviousValue(previous_value));
    }
    
    Ok(key)
}

/// Interprets a string like "1" or "0~1" or "5~" or "". Does not handle relative values.
fn interpret_event_value(value_str: &str, parser: &KeyParser) -> Result<Range, ArgumentError> {
    if ! parser.allow_ranges && value_str.contains('~') {
        return Err(ArgumentError::new(format!("No ranges are allowed in the value \"{}\".", value_str)));
    }
    
    let (min_value_str, max_value_str_opt) = utils::split_once(value_str, "~");
    let max_value_str = max_value_str_opt.unwrap_or(min_value_str);

    let min = parse_int_or_wildcard(min_value_str)?;
    let max = parse_int_or_wildcard(max_value_str)?;

    if let (Some(min_value), Some(max_value)) = (min, max) {
        if min_value > max_value {
            return Err(ArgumentError::new(format!(
                "The upper bound of a value range may not be smaller than its lower bound. Did you intend to use the range {}~{} instead?", max_value, min_value
            )));
        }
    }

    Ok(Range::new(min, max))
}

/// Returns None for "", an integer for integer strings, and otherwise gives an error.
fn parse_int_or_wildcard(value_str: &str) -> Result<Option<i32>, ArgumentError> {
    if value_str == "" {
        Ok(None)
    } else {
        let value: i32 = value_str.parse().map_err(|err| ArgumentError::new(
            format!("Cannot interpret {} as an integer: {}.", value_str, err)
        ))?;
        Ok(Some(value))
    }
}

enum AffineParseResult {
    // This value is an actual affine factor.
    IsAffine(KeyProperty),
    // This value turns out to be constant.
    IsConstant(KeyProperty),
    // This value is neither affine nor a constant, but it may be something else, like a range.
    Unparsable,
}

/// Parses a value like "0.1d" or "-x".
fn interpret_relative_value(value_str: &str) -> AffineParseResult {
    let factor = match crate::affine::parse_affine_factor(value_str) {
        Ok(factor) => factor,
        Err(_) => return AffineParseResult::Unparsable,
    };
    if let Some(value) = factor.as_constant() {
        if value.trunc() != value {
            return AffineParseResult::Unparsable;
        };
        let value_as_i32: i32 = value.trunc() as i32;
        AffineParseResult::IsConstant(KeyProperty::Value(Range::new(value_as_i32, value_as_i32)))
    } else {
        AffineParseResult::IsAffine(KeyProperty::AffineFactor(factor))
    }
}

#[test]
fn unittest_intersection() {
    let parser = KeyParser::default_filter();
    let expected_to_intersect = [
        ("key", "key"),
        ("key:a", "key:a"),
        ("key", "key:a"),
        ("key:a", "key"),
        ("key:a:1..2@foo", "key:a:1..2@foo"),
        ("key:a:1..2@foo", "@foo"),
        ("", ""),
        ("", "key:a:1..2@foo"),
        ("%1", "%1"),
        ("%1", "key"),
        ("%1", "key:left"),
        ("%1", "btn:left"),
        ("%1", ""),
    ];
    let expected_not_to_intersect = [
        ("key:a", "key:b"),
        ("key:a", "btn"),
        ("btn", "key:a"),
        ("key", "btn"),
        ("abs", "rel"),
        ("%1", "%2"),
        ("key:a@foo", "key:a@bar"),
        ("key:a@foo", "@bar"),
        ("key:a:1", "key:a:2"),
        ("key:a:1..2", "key:a:0..2"),
        ("%1", "abs:x"),
    ];

    for (key_1, key_2) in expected_to_intersect {
        assert!(parser.parse(key_1).unwrap().intersects_with(&parser.parse(key_2).unwrap()));
        assert!(parser.parse(key_2).unwrap().intersects_with(&parser.parse(key_1).unwrap()));
    }
    for (key_1, key_2) in expected_not_to_intersect {
        assert!(! parser.parse(key_1).unwrap().intersects_with(&parser.parse(key_2).unwrap()));
        assert!(! parser.parse(key_2).unwrap().intersects_with(&parser.parse(key_1).unwrap()));
    }
}07070100000044000081A4000000000000000000000001647DB23000001B8E000000000000000000000000000000000000002000000000evsieve-1.4.0~0/src/loopback.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::time::{Instant, Duration};
use std::convert::TryInto;
use std::num::NonZeroI32;

/// Whenever a wakeup is scheduled, you get a `Token` back. At the desired time, a wakeup()
/// call with the provided token shall be made.
#[derive(PartialEq, Eq)]
pub struct Token(u64);

impl Token {
    // The `clone()` implementation is private to avoid some errors that can happen from
    // carelessly cloning tokens.
    fn clone(&self) -> Token {
        Token(self.0)
    }
}

pub struct Loopback {
    schedule: Vec<(Instant, Token)>,

    /// A counter for the amount of `Token`s that were handed out. Ensures that all handed
    /// out tokens shall be unique except in case of integer overflow.
    token_index: u64,
}

/// A LoopbackHandle contains a reference to the Loopback device, plus a virtual moment that
/// is considered to be "now". A LoopbackHandle can be used to schedule a callback after some
/// time period in the future. The instant that the callback happens shall always be computed
/// relative to the virtual "now" instant, rather than the real now instand.
/// 
/// The virtual now instant may be different from the real now instant. The virtual now may be
/// older than the real now in case there is some kind of backlog, e.g. if a callback F was
/// supposed to be handled at time X, it is now time X+5, and handling the F causes another
/// callback G to be scheduled in 2 time units, then the callback G is scheduled at X+2 rather
/// than X+7. This helps to make sure that the A key always reaches the output before the B key
/// in cases like:
/// 
///     --map key:a key:a key:b
///     --delay key:a period=0.0005
///     --delay key:a period=0.0003
///     --delay key:b period=0.001
///     --output
/// 
pub struct LoopbackHandle<'a> {
    loopback: &'a mut Loopback,
    /// If Some, then we shall emulate the current time being a certain moment in time, even
    /// if it isn't that time right now. If it is None, then it represents the actual time
    /// of the current moment, but it has not been computed yet because that would cost a
    /// syscall and we're not actually sure if we'll actually need it, and it must be computed
    /// when we actually need it.
    now: Option<Instant>,
}

pub enum Delay {
    Now,
    Never,
    /// Wait a specified amount of milliseconds.
    Wait(NonZeroI32),
}

impl Loopback {
    pub fn new() -> Loopback {
        Loopback {
            schedule: Vec::new(),
            token_index: 0,
        }
    }

    pub fn time_until_next_wakeup(&self) -> Delay {
        let next_instant_opt = self.schedule.iter()
            .map(|(instant, _token)| instant).min();
        
        // If None, then then there are no events scheduled to happen.
        let next_instant = match next_instant_opt {
            Some(value) => value,
            None => return Delay::Never,
        };

        // If None, then the event should've been scheduled at some time in the past.
        let duration = match next_instant.checked_duration_since(Instant::now()) {
            Some(value) => value,
            None => return Delay::Now,
        };

        // If Err, then the delay is very, very far in the future. It probably means the user
        // entered some bogus number for the delay. Let's not panic.
        let millisecond_wait: i32 = match duration.as_millis().try_into() {
            Ok(value) => value,
            Err(_) => return Delay::Never,
        };

        // Ensure that we do not construct a NextEventDelay::Wait(0) result.
        match NonZeroI32::new(millisecond_wait) {
            Some(value) => Delay::Wait(value),
            None => Delay::Now,
        }
    }

    /// The most overdue token that is due or overdue and removes it from self's schedule.
    /// If two due tokens are due at the exact same time, returns them in the order they
    /// were added to the Loopback device.
    /// 
    /// The `now` argument denotes what the current time logically is. All events that are
    /// due before `now` will be added.
    /// 
    /// The reason this returns only one token is because it is possible that while processing
    /// that one token, new tokens get added to the schedule that are due before any other tokens
    /// that are actually due already. The new token should then be handled first, and that is
    /// not possible if this function were to return multiple tokens at once.
    pub fn poll_once(&mut self, now: Instant) -> Option<(Instant, Token)> {
        if self.schedule.is_empty() {
            return None;
        }

        let mut ready_tokens: Vec<(Instant, Token)> = Vec::new();
        let mut remaining_schedule: Vec<(Instant, Token)> = Vec::new();

        for (instant, token) in std::mem::take(&mut self.schedule) {
            if instant <= now {
                ready_tokens.push((instant, token));
            } else {
                remaining_schedule.push((instant, token));
            }
        }
        // Stably sort: make sure that the most overdue token is yielded first. Tokens that
        // are due at the exact same time should be yielded in the order they were added.
        ready_tokens.sort_by_key(|(time, _token)| *time);

        // Take the first ready token, add the rest back to the schedule.
        let mut ready_tokens_iter = ready_tokens.into_iter();
        let first_token = ready_tokens_iter.next();
        self.schedule = ready_tokens_iter.chain(remaining_schedule).collect();

        first_token
    }

    fn generate_token(&mut self) -> Token {
        if cfg!(debug_assertions) {
            self.token_index += 1;
        } else {
            self.token_index = self.token_index.wrapping_add(1);
        }
        Token(self.token_index)
    }

    pub fn get_handle(&mut self, now: Instant) -> LoopbackHandle {
        LoopbackHandle {
            loopback: self,
            now: Some(now),
        }
    }

    pub fn get_handle_lazy(&mut self) -> LoopbackHandle {
        LoopbackHandle {
            loopback: self,
            now: None,
        }
    }
}

impl<'a> LoopbackHandle<'a> {
    fn schedule_wakeup_at(&mut self, time: Instant) -> Token {
        let token = self.loopback.generate_token();
        self.loopback.schedule.push((time, token.clone()));
        token
    }

    pub fn schedule_wakeup_in(&mut self, delay: Duration) -> Token {
        let now = self.now();
        self.schedule_wakeup_at(now + delay)
    }

    /// If a previously-scheduled wakeup no longer seems needed, you can cancel it to save some
    /// CPU cycles later.
    pub fn cancel_token(&mut self, token: Token) {
        self.loopback.schedule.retain(|(_, other_token)| token != *other_token);
    }

    /// Like self.now, but lazily computes the current time if it wasn't already stored
    /// in self.now.
    fn now(&mut self) -> Instant {
        let time = match self.now {
            Some(time) => time,
            None => Instant::now(),
        };
        self.now = Some(time);
        time
    }
}07070100000045000081A4000000000000000000000001647DB230000038EA000000000000000000000000000000000000001C00000000evsieve-1.4.0~0/src/main.rs// SPDX-License-Identifier: GPL-2.0-or-later

// Allowed because useless default implementations are dead lines of code.
#![allow(clippy::new_without_default)]

// Allowed because the key "" is a canonically valid key, and comparing a key to "" is more
// idiomatic than asking whether a key is empty.
#![allow(clippy::comparison_to_empty)]

// Allowed because nested ifs allow for more-readable code.
#![allow(clippy::collapsible_if)]

// Disallowed for code uniformity.
#![warn(clippy::explicit_iter_loop)]
#![warn(clippy::explicit_into_iter_loop)]

pub mod event;
pub mod key;
pub mod domain;
pub mod state;
pub mod signal;
pub mod error;
pub mod capability;
pub mod affine;
pub mod range;
pub mod ecodes;
pub mod predevice;
pub mod subprocess;
pub mod daemon;
pub mod loopback;
pub mod stream;
pub mod control_fifo;
pub mod time;
pub mod utils;

#[cfg(feature = "auto-scan")]
pub mod scancodes;

pub mod io {
    pub mod input;
    pub mod epoll;
    pub mod output;
    pub mod internal_pipe;
    pub mod fd;
    pub mod fifo;
}

pub mod persist {
    pub mod inotify;
    pub mod blueprint;
    pub mod subsystem;
    pub mod interface;
}

pub mod arguments {
    pub mod hook;
    pub mod parser;
    pub mod input;
    pub mod output;
    pub mod lib;
    pub mod map;
    pub mod toggle;
    pub mod print;
    pub mod merge;
    pub mod delay;
    pub mod withhold;
    pub mod control_fifo;
    pub mod test;
    pub mod config;
}

pub mod bindings {
    #[allow(warnings)]
    pub mod libevdev;
}

#[macro_use]
extern crate lazy_static;

use std::os::unix::prelude::{AsRawFd, RawFd};

use arguments::parser::Implementation;
use error::{RuntimeError, Context};
use io::epoll::{Epoll, FileIndex, Message};
use io::fd::HasFixedFd;
use io::input::InputDevice;
use persist::interface::{HostInterfaceState};
use stream::Setup;
use signal::{SigMask, SignalFd};
use control_fifo::ControlFifo;

use crate::event::EventCode;
use crate::persist::subsystem::Report;
use crate::predevice::PersistMode;


fn main() {
    let result = run_and_interpret_exit_code();
    daemon::await_completion();
    subprocess::terminate_all();
    std::process::exit(result)
}

fn run_and_interpret_exit_code() -> i32 {
    let result = std::panic::catch_unwind(run);

    match result {
        Ok(Ok(())) => 0,
        // A RuntimeError happened.
        Ok(Err(error)) => {
            eprintln!("{}", error);
            1
        },
        // A panic happened.
        Err(_) => {
            eprintln!("Internal error: a panic happened. This is a bug.");
            1
        },
    }
}

pub enum Pollable {
    InputDevice(InputDevice),
    SignalFd(SignalFd),
    ControlFifo(ControlFifo),
    PersistSubsystem(persist::interface::HostInterface),
}
unsafe impl HasFixedFd for Pollable {}

impl AsRawFd for Pollable {
    fn as_raw_fd(&self) -> RawFd {
        match self {
            Pollable::InputDevice(device) => device.as_raw_fd(),
            Pollable::SignalFd(fd) => fd.as_raw_fd(),
            Pollable::ControlFifo(fifo) => fifo.as_raw_fd(),
            Pollable::PersistSubsystem(interface) => interface.as_raw_fd(),
        }
    }
}

struct Program {
    epoll: Epoll<Pollable>,
    setup: Setup,
    persist_subsystem: HostInterfaceState,
}

const TERMINATION_SIGNALS: [libc::c_int; 3] = [libc::SIGTERM, libc::SIGINT, libc::SIGHUP];

fn run() -> Result<(), RuntimeError> {
    // Check if the arguments contain --help or --version.
    let args: Vec<String> = std::env::args().skip(1).collect();
    if arguments::parser::check_help_and_version(&args) {
        daemon::notify_ready_async();
        return Ok(());
    }

    // Listen for signals sent to this program.
    let mut sigmask = SigMask::new();
    sigmask.add(libc::SIGPIPE);
    for &signal in &TERMINATION_SIGNALS {
        sigmask.add(signal);
    }
    let signal_fd = signal::SignalFd::new(&sigmask)?;
    let mut epoll = Epoll::new()?;
    epoll.add_file(Pollable::SignalFd(signal_fd))?;

    // Additionally block SIGCHLD because another thread listens for it.
    sigmask.add(libc::SIGCHLD);
    let _signal_block = unsafe { signal::SignalBlock::new(&sigmask)? };

    // Parse the arguments and set up the input/output devices.
    let Implementation { setup, input_devices, control_fifos } = arguments::parser::implement(args)?;
    for device in input_devices {
        epoll.add_file(Pollable::InputDevice(device))?;
    }
    for fifo in control_fifos {
        epoll.add_file(Pollable::ControlFifo(fifo))?;
    }

    // If the persistence subsystem is running, this shall keep track of its index in the epoll.
    let persist_subsystem: HostInterfaceState = HostInterfaceState::new();

    let mut program = Program {
        epoll, setup, persist_subsystem
    };
    daemon::notify_ready_async();

    // Make sure evsieve has something to do.
    if has_no_activity(&program.epoll) {
        println!("Warning: no input devices available. Evsieve will exit now.");
        return Ok(());
    }

    // Iterate over messages generated by the epoll.
    enter_main_loop(&mut program)?;

    // Shut down the persistence system properly.
    program.persist_subsystem.await_shutdown(&mut program.epoll);

    Ok(())
}

/// An enum used to signal to the main loop which action should be taken: if a function returns
/// Action::Continue, the program should go on, otherwise it should perform a clean exit.
enum Action {
    Continue,
    Exit,
}

/// The main loop of the program. Polls the epoll and handles it responses. Quits if an `Action::Exit`
/// is returned by `handle_ready_file()` or `handle_broken_file()`.
fn enter_main_loop(program: &mut Program) -> Result<(), RuntimeError> {
    loop {
        let timeout: i32 = match program.setup.time_until_next_wakeup() {
            loopback::Delay::Now => {
                stream::wakeup_until(&mut program.setup, crate::time::Instant::now());
                continue;
            },
            loopback::Delay::Never => crate::io::epoll::INDEFINITE_TIMEOUT,
            loopback::Delay::Wait(time) => time.get(),
        };

        let messages = program.epoll.poll(timeout).with_context("While polling the epoll for events:")?;

        for message in messages {
            let action = match message {
                Message::Ready(index) => {
                    match handle_ready_file(program, index) {
                        Ok(action) => action,
                        Err(error) => {
                            error.print_err();
                            handle_broken_file(program, index)
                        }
                    }
                },
                Message::Broken(index) => {
                    handle_broken_file(program, index)
                },
                Message::Hup(index) => {
                    match program.epoll.get(index) {
                        Some(Pollable::ControlFifo(_)) => {
                            // HUP for a control FIFO should never happen because we keep the FIFO open
                            // for writing ourselves in order to prevent HUP's from happening. If a HUP
                            // happens anyway, I suppose something is really wrong.
                            eprintln!("Warning: unexpected EPOLLHUP received on a control FIFO.");
                            handle_broken_file(program, index)
                        },
                        _ => handle_broken_file(program, index),
                    }
                },
            };

            match action {
                Action::Continue => continue,
                Action::Exit => return Ok(()),
            }
        }
    }
}

/// If this function returns Err, then `handle_broken_file` needs to be called with the same index.
/// IMPORTANT: this function should NOT return Err if the device at `index` itself is not broken.
/// If some other error occurs, you should handle it in this function itself and then return Ok.
fn handle_ready_file(program: &mut Program, index: FileIndex) -> Result<Action, RuntimeError> {
    let file = match program.epoll.get_mut(index) {
        Some(file) => file,
        None => {
            eprintln!("Internal error: an epoll reported ready on a device that is not registered with it. This is a bug.");
            return Ok(Action::Continue);
        },
    };
    match file {
        Pollable::InputDevice(device) => {
            let events = device.poll().with_context_of(||
                format!("While polling the input device {}:", device.path().display())
            )?;
            for (time, event) in events {
                stream::wakeup_until(&mut program.setup, time);
                stream::run(&mut program.setup, time, event);
            }
            Ok(Action::Continue)
        },
        Pollable::SignalFd(fd) => {
            let siginfo = fd.read_raw()?;
            let signal_no = siginfo.ssi_signo as i32;
            if TERMINATION_SIGNALS.contains(&signal_no) {
                Ok(Action::Exit)
            } else {
                // Ignore other signals, including SIGPIPE.
                Ok(Action::Continue)
            }
        },
        Pollable::ControlFifo(fifo) => {
            let commands = fifo.poll().with_context_of(
                || format!("While polling commands from {}:", fifo.path()),
            )?;
            for command in commands {
                command.execute(&mut program.setup)
                    .with_context("While executing a command:")
                    .print_err();
            }

            Ok(Action::Continue)
        },
        Pollable::PersistSubsystem(ref mut interface) => {
            let report = interface.recv().with_context("While polling the persistence subsystem from the main thread:")?;
            Ok(handle_persist_subsystem_report(program, index, report))
        },
    }
}

fn handle_broken_file(program: &mut Program, index: FileIndex) -> Action {
    let broken_device = match program.epoll.remove(index) {
        Some(file) => file,
        None => {
            eprintln!("Internal error: epoll reported a file as broken despite that file not being registered with said epoll.");
            return Action::Continue;
        }
    };
    match broken_device {
        Pollable::InputDevice(mut device) => {
            eprintln!("The device {} has been disconnected.", device.path().display());

            // Release all keys that this device had pressed, so we don't end up with a key stuck on
            // an output device.
            let pressed_keys: Vec<EventCode> = device.get_pressed_keys().collect();
            let now = crate::time::Instant::now();

            for key_code in pressed_keys {
                let release_event = device.synthesize_event(key_code, 0);
                stream::run(&mut program.setup, now, release_event);
            }
            stream::syn(&mut program.setup);

            match device.persist_mode() {
                // Mode None: drop the device and carry on without it, if possible.
                PersistMode::None => {},
                // Mode Exit: quit evsieve now.
                PersistMode::Exit => return Action::Exit,
                // Mode Reopen: try to reopen the device if it becomes available again later.
                PersistMode::Reopen => {
                    if let Some(interface) = program.persist_subsystem.require(&mut program.epoll) {
                        interface.add_blueprint(device.to_blueprint())
                            .with_context("While trying to register a disconnected device for reopening:")
                            .print_err()
                    } else {
                        eprintln!("Internal error: cannot reopen device: persistence subsystem not available.")
                    }
                }
            };
        },
        Pollable::ControlFifo(fifo) => {
            eprintln!("Error: the FIFO at {} is no longer available.", fifo.path());
        },
        Pollable::SignalFd(_fd) => {
            eprintln!("Fatal error: signal file descriptor broken.");
            return Action::Exit;
        },
        Pollable::PersistSubsystem(mut interface) => {
            eprintln!("Internal error: the persistence subsystem has broken. Evsieve may fail to open devices specified with the persist flag.");
            let _ = interface.request_shutdown();
            program.persist_subsystem.mark_as_broken();
        },
    }

    if has_no_activity(&program.epoll) {
        println!("No devices to poll events from. Evsieve will exit now.");
        Action::Exit
    } else {
        Action::Continue
    }
}

fn handle_persist_subsystem_report(program: &mut Program, index: FileIndex, report: Report) -> Action {
    match report {
        Report::Shutdown => {
            let _ = program.epoll.remove(index);
            program.persist_subsystem.mark_as_shutdown();
            Action::Continue
        },
        Report::BlueprintDropped => {
            if has_no_activity(&program.epoll) {
                println!("No devices remaining that can possibly generate events. Evsieve will exit now.");
                Action::Exit
            } else {
                Action::Continue
            }
        },
        Report::DeviceOpened(mut device) => {
            if let Err(error) = device.grab_if_desired() {
                error.with_context(format!("While grabbing the device {}:", device.path().display()))
                    .print_err();
                eprintln!("Warning: unable to reopen device {}. The device is most likely grabbed by another program.", device.path().display());
                return Action::Continue
            }

            let device_path = device.path().to_owned();
            program.setup.update_caps(&device);

            match program.epoll.add_file(Pollable::InputDevice(device))
            {
                Ok(_) => println!("The device {} has been reconnected.", device_path.display()),
                Err(error) => {
                    error.with_context("While adding a newly opened device to the epoll:").print_err();
                },
            }

            Action::Continue
        }
    }
}

/// Returns true if evsieve has nothing to do and should just exit.
fn has_no_activity(epoll: &Epoll<Pollable>) -> bool {
    for file in epoll.files() {
        match file {
            Pollable::InputDevice(_) => return false,
            Pollable::PersistSubsystem(_) => return false,
            Pollable::ControlFifo(_) => (),
            Pollable::SignalFd(_) => (),
        }
    }
    true
}
07070100000046000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001C00000000evsieve-1.4.0~0/src/persist07070100000047000081A4000000000000000000000001647DB230000006BC000000000000000000000000000000000000002900000000evsieve-1.4.0~0/src/persist/blueprint.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::io::input::{InputDevice, InputDeviceName};
use crate::predevice::PreInputDevice;
use crate::capability::Capabilities;
use crate::error::{SystemError};

/// Represents something can can be used to re-open a closed input device.
pub struct Blueprint {
    pub pre_device: PreInputDevice,
    pub capabilities: Capabilities,
    pub name: InputDeviceName,
}

impl Blueprint {
    /// Tries to reopen the device from which this blueprint was generated.
    /// On success, returns the device. On failure, returns Ok(None). In case of a grave
    /// error that signals reopening should not be retried, returns Err(SystemError).
    pub fn try_open(&self) -> Result<Option<InputDevice>, SystemError> {
        if ! self.pre_device.path.exists() {
            return Ok(None);
        }
        let input_device = InputDevice::open(self.pre_device.clone())?;

        // Do sanity checks.
        if input_device.name() != &self.name {
            println!(
                "Warning: the reconnected device \"{}\" has a different name than expected. Expected name: \"{}\", new name: \"{}\".",
                self.pre_device.path.display(),
                self.name.to_string_lossy(),
                input_device.name().to_string_lossy(),
            );
        }

        // TODO: LOW-PRIORITY this may print warnings on capabilities differing only in value.
        if *input_device.capabilities() != self.capabilities {
            println!(
                "Warning: the capabilities of the reconnected device \"{}\" are different than expected.",
                self.pre_device.path.display()
            );
        }
        
        Ok(Some(input_device))
    }
}07070100000048000081A4000000000000000000000001647DB230000011DE000000000000000000000000000000000000002A00000000evsieve-1.4.0~0/src/persist/bytestream.rs// SPDX-License-Identifier: GPL-2.0-or-later

//! A miniature serde-like implementation because the metric ton of dependencies serde brings
//! with it are unacceptable in this project.

use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::io::{Read, Write};

use crate::capability::Capabilities;

trait Serializable where Self: Sized {
    fn serialize(&self, stream: &mut dyn Write) -> std::io::Result<()>;
    fn deserialize(stream: &mut dyn Read) -> std::io::Result<Self>;
}

// We use a macro instead of a template implementation because there is no trait that guarantees
// the existence of to_le_bytes() and from_le_bytes().
macro_rules! impl_serialize_num {
    ($name:ident) => {
        impl Serializable for $name {
            fn serialize(&self, stream: &mut dyn Write) -> std::io::Result<()> {
                stream.write_all(&self.to_le_bytes())
            }
            fn deserialize(stream: &mut dyn Read) -> std::io::Result<Self> {
                let mut buffer: [u8; std::mem::size_of::<Self>()] = [0; std::mem::size_of::<Self>()];
                stream.read_exact(&mut buffer)?;
                Ok(Self::from_le_bytes(buffer))
            }
        }
    }
}

impl_serialize_num!(i64);
impl_serialize_num!(i32);
impl_serialize_num!(i16);
impl_serialize_num!(i8);
impl_serialize_num!(u64);
impl_serialize_num!(u32);
impl_serialize_num!(u16);
impl_serialize_num!(u8);

/// Format: an u64 denoting the length of the array, followed up by the members of the array.
impl<T: Serializable> Serializable for Vec<T> {
    fn serialize(&self, stream: &mut dyn Write) -> std::io::Result<()> {
        (self.len() as u64).serialize(stream)?;
        for item in self {
            item.serialize(stream)?;
        }
        Ok(())
    }
    fn deserialize(stream: &mut dyn Read) -> std::io::Result<Self> {
        let len = u64::deserialize(stream)? as usize;
        let mut result: Vec<T> = Vec::with_capacity(len);
        for _ in 0 .. len {
            result.push(T::deserialize(stream)?);
        }
        Ok(result)
    }
}

/// Format: an u64 denoting the amount of items in the set, followed up by the members of the set.
impl<T: Serializable + Eq + Hash> Serializable for HashSet<T> {
    fn serialize(&self, stream: &mut dyn Write) -> std::io::Result<()> {
        (self.len() as u64).serialize(stream)?;
        for item in self {
            item.serialize(stream)?;
        }
        Ok(())
    }
    fn deserialize(stream: &mut dyn Read) -> std::io::Result<Self> {
        let len = u64::deserialize(stream)? as usize;
        let mut result: HashSet<T> = HashSet::with_capacity(len);
        for _ in 0 .. len {
            let entry = T::deserialize(stream)?;
            if result.contains(&entry) {
                return Err(std::io::Error::new(std::io::ErrorKind::InvalidData,
                    "HashSet contains multiple copies of the same element."
                ));
            }
            result.insert(entry);
        }
        Ok(result)
    }
}

/// Format: an u64 denoting the amount of bytes in the string, followed up by the string in UTF-8 encoding.
impl Serializable for String {
    fn serialize(&self, stream: &mut dyn Write) -> std::io::Result<()> {
        let bytes_vec: Vec<u8> = self.as_bytes().to_vec();
        bytes_vec.serialize(stream)
    }
    fn deserialize(stream: &mut dyn Read) -> std::io::Result<Self> {
        let bytes_vec: Vec<u8> = Vec::<u8>::deserialize(stream)?;
        String::from_utf8(bytes_vec).map_err(|error| std::io::Error::new(
            std::io::ErrorKind::InvalidData, error
        ))
    }
}

impl<T: Serializable + Eq + Hash, U: Serializable> Serializable for HashMap<T, U> {
    fn serialize(&self, stream: &mut dyn Write) -> std::io::Result<()> {
        (self.len() as u64).serialize(stream)?;
        for (key, value) in self {
            key.serialize(stream)?;
            value.serialize(stream)?;
        }
        Ok(())
    }
    fn deserialize(stream: &mut dyn Read) -> std::io::Result<Self> {
        let len = u64::deserialize(stream)? as usize;
        let mut result: HashMap<T, U> = HashMap::with_capacity(len);
        for _ in 0 .. len {
            let key = T::deserialize(stream)?;
            if result.contains_key(&key) {
                return Err(std::io::Error::new(std::io::ErrorKind::InvalidData,
                    "HashMap contains multiple copies of the same key."
                ));
            }
            let value = U::deserialize(stream)?;
            result.insert(key, value);
        }
        Ok(result)
    }
}

07070100000049000081A4000000000000000000000001647DB2300000120A000000000000000000000000000000000000002600000000evsieve-1.4.0~0/src/persist/format.rs// SPDX-License-Identifier: GPL-2.0-or-later

// File format description.
//
// The is a binary file format. The file must start with the following six bytes as magic
// number to identify this file as an evsieve file:
//
// bf a5 7e 5d 02 e6
//
// Next follows an u16 which identifies the file version. The file version should be 1.
// If a higher number is read here, then the file is created by a future version of
// evsieve. This number, and all other numbers, must be encoded in low-endian format.
// Therefore, the next two bytes are:
//
// 01 00
//
// The next variable amount of  bytes represent the path of the device whose capabilities
// this file represents. The path MUST start with a / and MUST end with a null-byte. The
// length of the path is decided by finding the null byte. The path has no particular encoding,
// just like Linux paths don't have any encoding either.
//
// After the path's null byte, between 0 and 1 additional padding null bytes follow, until
// the current byte index is a multiple of 2.
//
// Then, an u16 integer $n$ follows, representing how many different types of events this device
// accepts. Thereafter, $n$ blocks follow representing the capabilities for each specific type,
// which has one of the following formats. All blocks must start with an u16 representing the
// event type; based on the event type, the block format is decided.
//
// A. Encoding generic capabilities.
//
// This block starts with an u16 representing the event type, followed up by an u16 $x$
// representing the amount of event codes with this type that are supported. This is followed
// up by $x$ u16's, each representing the code that this device supports. After those $2x$
// bytes, the next block starts.
//
// B. Encoding EV_ABS capabilities.
//
// TODO.
//
// C. Encoding EV_REP capabilities.
//
// TODO.

use std::path::Path;
use std::os::unix::ffi::OsStrExt;
use std::convert::TryInto;
use crate::event::{EventType, EventCode};
use crate::capability::Capabilities;
use crate::error::*;

const MAGIC_HEADER: [u8; 6] = [0xbf, 0xa5, 0x7e, 0x5d, 0x02, 0xe6];
const FORMAT_VERSION: u16 = 1;

pub fn encode(path: &Path, capabilities: &Capabilities) -> Result<(), RuntimeError> {
    let mut result: Vec<u8> = Vec::new();
    result.extend(&MAGIC_HEADER);
    write_u16(&mut result, FORMAT_VERSION);
    let path_bytes = path.as_os_str().as_bytes();

    // Enforce invariants on paths.
    const ASCII_SLASH_CODE: u8 = 0x2f;
    const NULL_BYTE: u8 = 0;
    if path_bytes[0] != ASCII_SLASH_CODE {
        return Err(InternalError::new(format!(
            "Cannot encode the path {}: path does not start with a /.", path.to_string_lossy()
        )).into());
    }

    if path_bytes.contains(&NULL_BYTE) {
        return Err(InternalError::new(format!(
            "Cannot encode the path {}: path contains a null byte.", path.to_string_lossy()
        )).into());
    }

    // Add the path, the null terminator, and the eventual padding byte.
    result.extend(path_bytes);
    write_u8(&mut result, NULL_BYTE);
    if result.len() %2 == 1 {
        write_u8(&mut result, NULL_BYTE);
    }

    let supported_types = capabilities.ev_types();
    for ev_type in supported_types {
        match ev_type {
            _ => encode_generic_event_type(&mut result, ev_type, &capabilities)?,
        }
    }


    Ok(())
}

fn encode_generic_event_type(
        result: &mut Vec<u8>, ev_type: EventType, capabilities: &Capabilities
) -> Result<(), RuntimeError> {
    // Get the supported codes as a vector of u16.
    let mut supported_codes: Vec<u16> = capabilities.codes
        .iter().filter(|code| code.ev_type() == ev_type)
        .map(|code| code.code())
        .collect();

    // Not strictly necessary, but helps ensuring that encoding the same device results
    // into the same file, instead of always having the codes in random order.
    supported_codes.sort_unstable();
    supported_codes.dedup();
    
    let num_supported_codes: u16 = match supported_codes.len().try_into() {
        Ok(value) => value,
        Err(_) => {
            // TODO: More helpful error message? Or choose u32 instead?
            return Err(InternalError::new(format!(
                "Cannot encode event type: too many event codes.",
            )).into());
        },
    };

    write_u16(result, num_supported_codes);
    for code in supported_codes {
        write_u16(result, code);
    }

    Ok(())
}

fn write_u16(buffer: &mut Vec<u8>, value: u16) {
    buffer.extend(value.to_le_bytes());
}
fn write_u8(buffer: &mut Vec<u8>, value: u8) {
    buffer.extend(value.to_le_bytes());
}
0707010000004A000081A4000000000000000000000001647DB23000001235000000000000000000000000000000000000002700000000evsieve-1.4.0~0/src/persist/inotify.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::error::{SystemError, InternalError, RuntimeError};
use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd};


pub struct Inotify {
    fd: RawFd,
    /// Maps a watch id to a list of all paths that are watched by that id.
    watches: HashMap<i32, Vec<String>>,
}

impl Inotify {
    pub fn new() -> Result<Inotify, SystemError> {
        let fd = unsafe { libc::inotify_init1(libc::IN_NONBLOCK) };
        if fd < 0 {
            return Err(SystemError::os_with_context("While initializing an inotify instance:"));
        }
        Ok(Inotify { fd, watches: HashMap::new() })
    }

    /// Returns an inotify that watches all interesting input directories.
    pub fn for_input_dirs() -> Result<Inotify, SystemError> {
        let mut inotify = Inotify::new()?;
        inotify.add_watch("/dev/input".into())?;
        inotify.add_watch("/dev/input/by-id".into())?;
        Ok(inotify)
    }

    pub fn add_watch(&mut self, path: String) -> Result<(), SystemError> {
        let cstr = match std::ffi::CString::new(path.clone()) {
            Ok(value) => value,
            Err(_) => return Err(SystemError::new("Could not convert a string to a CString."))
        };

        let watch = unsafe {
            libc::inotify_add_watch(
                self.fd,
                cstr.as_ptr(),
                libc::IN_CREATE | libc::IN_MOVED_TO
            )
        };
        if watch < 0 {
            return Err(SystemError::os_with_context(format!(
                "While trying to add \"{}\" to an inotify instance:", path)))
        }
        self.watches.entry(watch).or_default().push(path);
        Ok(())
    }

    pub fn remove_watch(&mut self, path: String) -> Result<(), RuntimeError> {
        // Pre-cache the watch ids so we don't have to borrow self.watches during the loop.
        let watch_ids: Vec<i32> = self.watches.keys().cloned().collect();
        for watch_id in watch_ids {
            let paths = match self.watches.get_mut(&watch_id) {
                Some(paths) => paths,
                None => return Err(InternalError::new("A watch was unexpectedly removed from an Inotify.").into()),
            };
            if paths.contains(&path) {
                paths.retain(|item| item != &path);
                if paths.is_empty() {
                    self.remove_watch_by_id(watch_id)?;
                }
            }
        }

        Ok(())
    }

    fn remove_watch_by_id(&mut self, watch_id: i32) -> Result<(), SystemError> {
        // The error cases should be: self.fd is not valid, watch is not valid.
        // In either case, it is fine that watch is removed from self.watches in case of error.
        let res = unsafe { libc::inotify_rm_watch(self.fd, watch_id) };
        self.watches.remove(&watch_id);

        if res < 0 {
            Err(std::io::Error::last_os_error().into())
        } else {
            Ok(())
        }
    }

    pub fn watched_paths(&self) -> impl Iterator<Item=&String> {
        self.watches.values().flatten()
    }

    /// Adds all watches in the given vector, and removes all not in the given vector.
    pub fn set_watched_paths(&mut self, paths: Vec<String>) -> Result<(), RuntimeError> {
        let paths_to_remove: Vec<String> = self.watched_paths()
            .filter(|&path| !paths.contains(path))
            .cloned().collect();
        for path in paths_to_remove {
            self.remove_watch(path)?;
        }

        let watched_paths: Vec<&String> = self.watched_paths().collect();
        let paths_to_add: Vec<String> = paths.iter()
            .filter(|path| !watched_paths.contains(path))
            .cloned().collect();
        for path in paths_to_add {
            self.add_watch(path)?;
        }
        Ok(())
    }

    /// Does nothing besides clearing out the queued events.
    pub fn poll(&mut self) -> Result<(), SystemError> {
        const NAME_MAX: usize = 255;
        const BUFFER_SIZE: usize = std::mem::size_of::<libc::inotify_event>() + NAME_MAX + 1;
        let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
        let res = unsafe {
            libc::read(self.fd, buffer.as_mut_ptr() as *mut libc::c_void, BUFFER_SIZE)
        };

        if res < 0 {
            Err(SystemError::os_with_context("While reading from an inotify instance:"))
        } else {
            Ok(())
        }
    }
}

impl AsRawFd for Inotify {
    fn as_raw_fd(&self) -> RawFd {
        self.fd
    }
}

impl Drop for Inotify {
    fn drop(&mut self) {
        // Ignore any errors because we can't do anything about them.
        unsafe { libc::close(self.fd); }
    }
}0707010000004B000081A4000000000000000000000001647DB2300000105C000000000000000000000000000000000000002900000000evsieve-1.4.0~0/src/persist/interface.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::persist::subsystem::{Command, Report};
use crate::persist::blueprint::Blueprint;
use crate::io::internal_pipe::{Sender, Receiver};
use crate::io::epoll::{Epoll, FileIndex};
use crate::{Pollable, error::*};
use std::thread::JoinHandle;
use std::os::unix::io::{AsRawFd, RawFd};

/// The main thread controls the persistence subsystem through this struct.
pub struct HostInterface {
    pub(super) commander: Sender<Command>,
    pub(super) reporter: Receiver<Report>,
    pub(super) join_handle: JoinHandle<()>,
}

impl HostInterface {
    /// Asks the subsystem to try to reopen this blueprint.
    pub fn add_blueprint(&mut self, blueprint: Blueprint) -> Result<(), SystemError> {
        self.commander.send(Command::AddBlueprint(blueprint))
    }

    /// Asks the subsystem to start shutting down. Does not wait until it has actually shut down.
    pub fn request_shutdown(&mut self) -> Result<(), SystemError> {
        self.commander.send(Command::Shutdown)
    }

    pub fn await_shutdown(mut self) {
        if self.request_shutdown().is_ok() {
            let _ = self.join_handle.join();
        }
    }

    pub fn recv(&mut self) -> Result<Report, SystemError> {
        self.reporter.recv()
    }
}

impl AsRawFd for HostInterface {
    fn as_raw_fd(&self) -> RawFd {
        self.reporter.as_raw_fd()
    }
}

pub enum HostInterfaceState {
    /// The persistence subsystem has never been started yet because it wasn't needed so far.
    NotStarted,
    /// The persistence subsystem is currently running and registered with a certain Epoll at a given index.
    Running(FileIndex),
    /// The persistence subsystem has crashed.
    Error,
    /// The persistence subsystem has successfully shut down.
    Shutdown,
}

impl HostInterfaceState {
    pub fn new() -> HostInterfaceState {
        HostInterfaceState::NotStarted
    }

    /// Returns a reference to a HostInterface registered with a certain Epoll. Never call this
    /// function with two different epolls through the lifetime of self.
    pub fn require<'a>(&mut self, epoll: &'a mut Epoll<Pollable>) -> Option<&'a mut HostInterface> {
        use HostInterfaceState::*;

        // Start the subsystem if it is not already running.
        if let NotStarted = self {
            let interface = match crate::persist::subsystem::launch() {
                Ok(interface) => interface,
                Err(error) => {
                    eprintln!("Warning: failed to start the persistence subsystem. Devices with the persist flag may not be (re)opened successfully.");
                    error.print_err();
                    *self = Error;
                    return None;
                }
            };
            let index = match epoll.add_file(crate::Pollable::PersistSubsystem(interface)) {
                Ok(index) => index,
                Err(error) => {
                    error.with_context("While adding the persistence subsystem interface to an epoll:").print_err();
                    *self = Error;
                    return None;
                }
            };
            *self = Running(index);
        }

        self.get(epoll)
    }

    pub fn get<'a>(&mut self, epoll: &'a mut Epoll<Pollable>) -> Option<&'a mut HostInterface> {
        use HostInterfaceState::*;
        match self {
            Running(index) => {
                if let Some(crate::Pollable::PersistSubsystem(ref mut interface)) = epoll.get_mut(*index) {
                    Some(interface)
                } else {
                    None
                }
            },
            NotStarted => None,
            Error => None,
            Shutdown => None,
        }
    }

    pub fn mark_as_broken(&mut self) {
        *self = HostInterfaceState::Error;
    }

    pub fn mark_as_shutdown(&mut self) {
        *self = HostInterfaceState::Shutdown;
    }

    pub fn await_shutdown(self, epoll: &mut Epoll<Pollable>) {
        if let HostInterfaceState::Running(index) = self {
            if let Some(Pollable::PersistSubsystem(interface)) = epoll.remove(index) {
                interface.await_shutdown();
            }
        }
    }
}0707010000004C000081A4000000000000000000000001647DB23000003336000000000000000000000000000000000000002900000000evsieve-1.4.0~0/src/persist/subsystem.rs// SPDX-License-Identifier: GPL-2.0-or-later

//! The persistence subsystem is in charge of taking Blueprints of unavailable InputDevices and waiting
//! until those devices become available so that the Blueprint can be opened. This happens in a separate
//! thread which communicates with the main thread through message passing.
//!
//! The system does not need to be launched until you actually want to open a blueprint, and ideally
//! should not be launched before then either, as that would waste system resources by having a useless
//! thread hanging around.

use crate::io::fd::HasFixedFd;
use crate::io::input::InputDevice;
use crate::io::internal_pipe;
use crate::io::internal_pipe::{Sender, Receiver};
use crate::persist::blueprint::Blueprint;
use crate::persist::inotify::Inotify;
use crate::persist::interface::HostInterface;
use crate::error::{Context, RuntimeError, SystemError};
use crate::io::epoll::{Epoll, Message};
use std::collections::HashSet;
use std::path::PathBuf;
use std::os::unix::io::{AsRawFd, RawFd};

/// Commands that the main thread can send to this subsystem.
pub enum Command {
    /// Requests this subsystem to try to reopen this blueprint.
    AddBlueprint(Blueprint),
    /// Requests this subsystem to halt.
    Shutdown,
}

/// Reports that this subsystem sends back to the main thread.
#[allow(clippy::large_enum_variant)]
pub enum Report {
    /// A device has been opened.
    DeviceOpened(InputDevice),
    /// A blueprint has been deemed unopenable and has been dropped.
    BlueprintDropped,
    /// This subsystem has shut down or almost shut down. There are no ongoing processes or destructors
    /// left to run that could cause trouble if the program were to exit() now.
    Shutdown,
}

enum Pollable {
    Command(Receiver<Command>),
    Daemon(Daemon),
}

impl AsRawFd for Pollable {
    fn as_raw_fd(&self) -> RawFd {
        match self {
            Pollable::Command(receiver) => receiver.as_raw_fd(),
            Pollable::Daemon(daemon) => daemon.as_raw_fd(),
        }
    }
}
unsafe impl HasFixedFd for Pollable {}

pub struct Daemon {
    blueprints: Vec<Blueprint>,
    inotify: Inotify,
}

/// Launches the persistence subsystem and returns an interface to communicate with the main thread.
pub fn launch() -> Result<HostInterface, SystemError> {
    let (commander, comm_in) = internal_pipe::channel()?;
    let (mut comm_out, reporter) = internal_pipe::channel()?;

    let join_handle = std::thread::spawn(move || {
        // Asserting unwind safety for Sender. My reasons for this are a bit wobbly, but I looked at
        // its source and all visible actions it takes appear to be atomic, e.g. a message is either sent
        // or not. I can't think of a scenario where a panic at any point could violate safety.
        let panic_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            start_worker(comm_in, &mut comm_out)
                .with_context("In the persistence subsystem:")
                .print_err();
        }));

        comm_out.send(Report::Shutdown).print_err();

        if let Err(payload) = panic_result {
            std::panic::resume_unwind(payload);
        }
    });

    Ok(HostInterface { commander, reporter, join_handle })
}


fn start_worker(comm_in: Receiver<Command>, comm_out: &mut Sender<Report>) -> Result<(), RuntimeError> {
    let daemon = Daemon::new()?;
    let mut epoll = Epoll::new()?;
    let daemon_index = epoll.add_file(Pollable::Daemon(daemon))?;
    epoll.add_file(Pollable::Command(comm_in))?;
    
    loop {
        let (commands, mut reports) = poll(&mut epoll)?;
        for command in commands {
            match command {
                Command::Shutdown => return Ok(()),
                Command::AddBlueprint(blueprint) => match &mut epoll[daemon_index] {
                    Pollable::Daemon(daemon) => {
                        daemon.add_blueprint(blueprint)?;
                        // Immediately try to open all blueprints after adding one, otherwise it is
                        // possible to fail to notice an blueprint becoming available if the associated
                        // events were already fired before it was added to the daemon.
                        try_open_and_report(daemon, &mut reports)?;
                    },
                    _ => unreachable!(),
                }
            }
        }
        for report in reports {
            comm_out.send(report)?;
        }
    }
}

fn poll(epoll: &mut Epoll<Pollable>) -> Result<(Vec<Command>, Vec<Report>), RuntimeError> {
    let mut commands: Vec<Command> = Vec::new();
    let mut reports: Vec<Report> = Vec::new();

    match epoll.poll(crate::io::epoll::INDEFINITE_TIMEOUT) {
        Err(error) => {
            error.with_context("While the persistence subsystem was polling for events:").print_err();
            commands.push(Command::Shutdown);
        },
        Ok(messages) => for message in messages {
            match message {
                Message::Broken(_index) => return Err(SystemError::new("Persistence daemon broken.").into()),
                Message::Ready(index) | Message::Hup(index) => match &mut epoll[index] {
                    Pollable::Daemon(daemon) => {
                        daemon.poll()?;
                        try_open_and_report(daemon, &mut reports)?
                    },
                    Pollable::Command(receiver) => {
                        match receiver.recv() {
                            Ok(command) => commands.push(command),
                            Err(error) => return Err(error.into()),
                        }
                    }
                }
            }
        }
    }
    Ok((commands, reports))
}

/// A wrapper around Daemon::try_open() that conveniently turns opened/broken devices into reports and
/// has the standard Rust error handling mechanism. Notably, if an error is encountered at some point,
/// the `reports` vector may still be modified to include progress that was made before the error happened.
fn try_open_and_report(daemon: &mut Daemon, reports: &mut Vec<Report>) -> Result<(), RuntimeError> {
    let TryOpenResult {
        opened_devices,
        broken_blueprints,
        error_encountered,
    } = daemon.try_open();

    reports.extend(opened_devices.into_iter().map(Report::DeviceOpened));
    reports.extend(broken_blueprints.into_iter().map(|_| Report::BlueprintDropped));

    match error_encountered {
        Some(err) => Err(err),
        None => Ok(()),
    }
}

struct TryOpenResult {
    opened_devices: Vec<InputDevice>,
    broken_blueprints: Vec<Blueprint>,
    error_encountered: Option<RuntimeError>,
}

impl Daemon {
    pub fn new() -> Result<Daemon, SystemError> {
        Ok(Daemon {
            blueprints: Vec::new(),
            inotify: Inotify::new()?,
        })
    }

    pub fn add_blueprint(&mut self, blueprint: Blueprint) -> Result<(), RuntimeError> {
        self.blueprints.push(blueprint);
        self.update_watches()?;
        Ok(())
    }

    /// Does nothing but clearing out the queued events. Call Daemon::try_open() to try to actually
    /// open the associated blueprints.
    pub fn poll(&mut self) -> Result<(), SystemError> {
        self.inotify.poll()
    }

    /// Checks whether it is possible to open some of the blueprints registered with this daemon,
    /// and opens them if it is.
    ///
    /// Does not clear out the associated Inotify's event queue. Make sure to call Daemon::poll() to do
    /// that as well in case an Epoll identifies this Daemon as ready.
    ///
    /// Returns three things:
    /// 1. A Vec of all devices that were successfully opened and should be sent to the main thread.
    /// 2. A Vec of all blueprints that are considered "broken" and should not be tried to be opened again.
    /// 3. If something went wrong, an error. The Result<> wrapper is not used because we don't want
    ///    successfully opened devices to just disappear if an error happened later.
    fn try_open(&mut self) -> TryOpenResult {
        const MAX_TRIES: usize = 5;
        let mut result = TryOpenResult {
            opened_devices: Vec::new(),
            broken_blueprints: Vec::new(),
            error_encountered: None,
        };

        for _ in 0 .. MAX_TRIES {
            // Try to open the devices.
            let mut remaining_blueprints = Vec::new();
            for blueprint in self.blueprints.drain(..) {
                match blueprint.try_open() {
                    Ok(Some(device)) => result.opened_devices.push(device),
                    Ok(None) => remaining_blueprints.push(blueprint),
                    Err(error) => {
                        error.print_err();
                        result.broken_blueprints.push(blueprint);
                    }
                }
            }
            self.blueprints = remaining_blueprints;
            
            // Just in case the relevant paths change between now and when we actually watch them
            // thanks to a race-condition, we do this within a loop until the paths are identical
            // for two iterations.
            match self.update_watches() {
                Ok(false) => return result, // The paths are identical.
                Ok(true) => (),             // The paths changed, we should re-scan.
                Err(error) => {             // Something went seriously wrong.
                    result.error_encountered = Some(error);
                    return result;
                }
            }
        }

        crate::utils::warn_once("Warning: maximum try count exceeded while listening for new devices.");
        result
    }

    /// Find out which paths may cause a change, then watch them.
    /// Returns true if the watched patch changed, otherwise returns false.
    fn update_watches(&mut self) ->  Result<bool, RuntimeError> {
        let paths_to_watch: Vec<String> = self.get_paths_to_watch();
            let paths_to_watch_hashset: HashSet<&String> = paths_to_watch.iter().collect();
            let paths_already_watched: HashSet<&String> = self.inotify.watched_paths().collect();

            if paths_to_watch_hashset == paths_already_watched {
                Ok(false)
            } else {
                self.inotify.set_watched_paths(paths_to_watch)?;
                Ok(true)
            }
    }

    pub fn get_paths_to_watch(&mut self) -> Vec<String> {
        let mut traversed_directories: Vec<String> = Vec::new();

        for blueprint in &mut self.blueprints {
            let paths = walk_symlink(blueprint.pre_device.path.clone());
            let mut directories = paths.into_iter()
                .filter_map(|mut path| {
                    path.pop();
                    match path.into_os_string().into_string() {
                        Ok(string) => Some(string),
                        // Unfortunately the ill-designed Rust standard library does not provide means
                        // to convert a OsString to a CString without converting it to String first.
                        // This makes Evsieve unable to deal with non-UTF8 paths. This bug is sufficiently
                        // low-priority that I cannot be bothered to fix it until Rust fixes their standard
                        // library by adding direct OsString -> CString conversion.
                        Err(os_string) => {
                            let warning_message = format!(
                                "Error: unable to deal with non-UTF8 path \"{}\".",
                                os_string.to_string_lossy()
                            );
                            crate::utils::warn_once(warning_message);
                            None
                        },
                    }
                });
            traversed_directories.extend(&mut directories);
        }

        traversed_directories.sort_unstable();
        traversed_directories.dedup();
        
        traversed_directories
    }
}

impl AsRawFd for Daemon {
    fn as_raw_fd(&self) -> RawFd {
        self.inotify.as_raw_fd()
    }
}

/// Returns a vector of all paths that lie in the chain of symlinks starting at `path`.
fn walk_symlink(path: PathBuf) -> Vec<PathBuf> {
    const MAX_SYMLINKS: usize = 20;

    // Walk down the chain of symlinks starting at path.
    let mut current_path: PathBuf = path.clone();
    let mut traversed_paths: Vec<PathBuf> = vec![current_path.clone()];

    while let Ok(next_path_rel) = current_path.read_link() {
        current_path.pop();
        current_path = current_path.join(next_path_rel);
        
        if traversed_paths.contains(&current_path) {
            break;
        }
        traversed_paths.push(current_path.clone());
        
        // The +1 is because the device node is not a symlink.
        if traversed_paths.len() > MAX_SYMLINKS + 1 {
            crate::utils::warn_once(format!(
                "Warning: too many symlinks encountered while resolving \"{}\".", path.display()
            ));
            break;
        }
    }

    traversed_paths
}0707010000004D000081A4000000000000000000000001647DB23000000A6C000000000000000000000000000000000000002100000000evsieve-1.4.0~0/src/predevice.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::domain::Domain;
use std::path::PathBuf;

/// Represents whether and how the user has requested the device to be grabbed.
/// Set through the grab flag or grab= clause on --input arguments.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum GrabMode {
    /// The user has requested this device to be grabbed immediately.
    Force,
    /// The user has requested this device to be grabbed once no EV_KEY keys are pressed.
    Auto,
    /// The user has requested this device not be grabbed.
    None,
}

impl GrabMode {
    /// If some input device is specified multiple times with different grab mode,
    /// this function finds the strongest of both of them.
    pub fn combine(first: GrabMode, second: GrabMode) -> GrabMode {
        if first == GrabMode::Force || second == GrabMode::Force {
            GrabMode::Force
        } else if first == GrabMode::Auto || second == GrabMode::Auto {
            GrabMode::Auto
        } else {
            GrabMode::None
        }
    }
}

/// Represents what should happen if the device is not available.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum PersistMode {
    /// Remove the device from the processing stream at runtime, or throw an error at startup time.
    None,
    /// Try to reattach the device at runtime, or throw an error at startup time.
    Reopen,
    /// If a device with mode exit disconnects, evsieve shall exit, even if other devices are still available.
    Exit,
}

#[derive(Clone)]
pub struct PreInputDevice {
    /// The path to this device.
    pub path: PathBuf,
    /// The domain that all events emitted by this device shall have.
    pub domain: Domain,
    /// Whether and how the user has requested this InputDevice be grabbed.
    pub grab_mode: GrabMode,
    /// What should be done if the device is disconnected while running.
    pub persist_mode: PersistMode,
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum RepeatMode {
    /// The kernel shall generate repeat events for this device.
    Enable,
    /// No repeat events shall be generated by this device.
    Disable,
    /// This device shall not be given a repeat capability, but repeat events shall be written to it.
    Passive,
}

pub struct PreOutputDevice {
    /// All events with this domain shall be written to this device.
    pub domain: Domain,
    /// If Some, the user has requested a symlink to the device to be created at the given path.
    pub create_link: Option<PathBuf>,
    /// The output device will be given this name.
    pub name: String,
    /// Determined by "repeat" or "norepeat" flags on output devices.
    pub repeat_mode: RepeatMode,
}0707010000004E000081A4000000000000000000000001647DB2300000362E000000000000000000000000000000000000001D00000000evsieve-1.4.0~0/src/range.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::cmp::Ord;
use std::cmp::Ordering;

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum ExtendedInteger {
    PositiveInfinity,
    NegativeInfinity,
    Discrete(i32),
}

impl ExtendedInteger {
    /// Returns its own value if discrete, otherwise returns a given default value.
    pub fn discrete_or(self, value: i32) -> i32 {
        match self {
            ExtendedInteger::Discrete(discrete_value) => discrete_value,
            _ => value,
        }
    }

    /// Returns true is self and other differ by exactly one.
    pub fn is_acjadent(self, other: ExtendedInteger) -> bool {
        match (self, other) {
            (ExtendedInteger::Discrete(value), ExtendedInteger::Discrete(other_value)) => {
                match value.checked_sub(other_value) {
                    Some(difference) => difference.abs() == 1,
                    None => false,
                }
            },
            _ => false,
        }
    }

    // Returns None for infinity minus infinity cases, otherwise subtracts two numbers. Overflows to Infinity.
    pub fn checked_sub(self, other: ExtendedInteger) -> Option<ExtendedInteger> {
        match self {
            ExtendedInteger::PositiveInfinity => match other {
                ExtendedInteger::PositiveInfinity => None,
                ExtendedInteger::Discrete(_) | ExtendedInteger::NegativeInfinity => Some(ExtendedInteger::PositiveInfinity),
            },
            ExtendedInteger::NegativeInfinity => match other {
                ExtendedInteger::NegativeInfinity => None,
                ExtendedInteger::Discrete(_) | ExtendedInteger::PositiveInfinity => Some(ExtendedInteger::NegativeInfinity),
            },
            ExtendedInteger::Discrete(value) => match other {
                ExtendedInteger::PositiveInfinity => Some(ExtendedInteger::NegativeInfinity),
                ExtendedInteger::NegativeInfinity => Some(ExtendedInteger::PositiveInfinity),
                ExtendedInteger::Discrete(other_value) => match value.checked_sub(other_value) {
                    Some(difference) => Some(ExtendedInteger::Discrete(difference)),
                    // If there is an integer overflow while substracting discrete values, wrap to infinity.
                    None => if self > other {
                        Some(ExtendedInteger::PositiveInfinity)
                    } else {
                        Some(ExtendedInteger::NegativeInfinity)
                    }
                }
            }
        }
    }

    /// Multiplies self with an f64 and truncates the result.
    pub fn mul_f64_round(self, other: f64, rounding_function: impl Fn(f64) -> f64) -> ExtendedInteger {
        match other.partial_cmp(&0.0) {
            None => self, // other is NaN
            Some(Ordering::Equal) => ExtendedInteger::Discrete(0),
            Some(Ordering::Greater) => match self {
                ExtendedInteger::Discrete(value) => ExtendedInteger::Discrete(
                    rounding_function(value as f64 * other) as i32
                ),
                _ => self,
            },
            Some(Ordering::Less) => match self {
                ExtendedInteger::Discrete(value) => ExtendedInteger::Discrete(
                    rounding_function(value as f64 * other) as i32
                ),
                _ => -self,
            }
        }
    }
}

impl From<i32> for ExtendedInteger {
    fn from(value: i32) -> ExtendedInteger {
        ExtendedInteger::Discrete(value)
    }
}

impl From<ExtendedInteger> for f64 {
    fn from(value: ExtendedInteger) -> Self {
        match value {
            ExtendedInteger::Discrete(val) => val.into(),
            ExtendedInteger::PositiveInfinity => Self::INFINITY,
            ExtendedInteger::NegativeInfinity => Self::NEG_INFINITY,
        }
    }
}

impl PartialOrd for ExtendedInteger {
    fn partial_cmp(&self, other: &ExtendedInteger) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for ExtendedInteger {
    fn cmp(&self, other: &ExtendedInteger) -> Ordering {
        match self {
            ExtendedInteger::PositiveInfinity => match other {
                ExtendedInteger::PositiveInfinity => Ordering::Equal,
                _ => Ordering::Greater,
            },
            ExtendedInteger::NegativeInfinity => match other {
                ExtendedInteger::NegativeInfinity => Ordering::Equal,
                _ => Ordering::Less,
            },
            ExtendedInteger::Discrete(value) => match other {
                ExtendedInteger::PositiveInfinity => Ordering::Less,
                ExtendedInteger::NegativeInfinity => Ordering::Greater,
                ExtendedInteger::Discrete(other_value) => value.cmp(other_value)
            }
        }
    }
}

impl std::ops::Neg for ExtendedInteger {
    type Output = ExtendedInteger;
    fn neg(self) -> Self::Output {
        match self {
            ExtendedInteger::PositiveInfinity => ExtendedInteger::NegativeInfinity,
            ExtendedInteger::NegativeInfinity => ExtendedInteger::PositiveInfinity,
            ExtendedInteger::Discrete(value) => ExtendedInteger::Discrete(-value),
        }
    }
}

impl std::ops::Sub<i32> for ExtendedInteger {
    type Output = ExtendedInteger;
    fn sub(self, rhs: i32) -> Self::Output {
        match self {
            ExtendedInteger::PositiveInfinity => self,
            ExtendedInteger::NegativeInfinity => self,
            ExtendedInteger::Discrete(value) => match value.checked_sub(rhs) {
                Some(result) => ExtendedInteger::Discrete(result),
                None => if rhs > 0 {
                    ExtendedInteger::NegativeInfinity
                } else {
                    ExtendedInteger::PositiveInfinity
                }
            }
        }
    }
}

/// A bound for the values of an Event's current value or previous value.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct Range {
    /// The values min and max are inclusive bounds.
    pub min: ExtendedInteger,
    pub max: ExtendedInteger,
}

impl Range {
    pub fn new(min: impl Into<Option<i32>>, max: impl Into<Option<i32>>) -> Range {
        Range {
            min: match min.into() {
                Some(value) => ExtendedInteger::Discrete(value),
                None => ExtendedInteger::NegativeInfinity,
            },
            max: match max.into() {
                Some(value) => ExtendedInteger::Discrete(value),
                None => ExtendedInteger::PositiveInfinity,
            },
        }
    }

    /// Returns a range that lies between the two provided integers. The two integers
    /// do not need to be provided in ascending order.
    pub fn spanned_between(a: ExtendedInteger, b: ExtendedInteger) -> Range {
        let max = std::cmp::max(a, b);
        let min = std::cmp::min(a, b);
        Range { min, max }
    }

    /// Checks whether this Range contains a value.
    pub fn contains(&self, value: i32) -> bool {
        let extended_value: ExtendedInteger = value.into();
        self.min <= extended_value && self.max >= extended_value
    }

    /// Returns the closest integer to value that lies within this Range.
    pub fn bound(&self, value: i32) -> i32 {
        if let ExtendedInteger::Discrete(min_value) = self.min {
            if value < min_value {
                return min_value;
            }
        }
        if let ExtendedInteger::Discrete(max_value) = self.max {
            if value > max_value {
                return max_value;
            }
        }
        value
    }

    /// The maximum difference between two event values that can fall in this range, which is one less
    /// than the total amount of event values that can fall in this range.
    ///
    /// Returns zero for (infinity, infinity) or (-infinity, -infinity) ranges because there is not a
    /// single event value that fall in that range.
    pub fn span(&self) -> ExtendedInteger {
        match self.max.checked_sub(self.min) {
            None => ExtendedInteger::Discrete(0),
            Some(value) => value,
        }
    }

    /// A range that contains every possible difference between two event codes that fall in this range.
    pub fn delta_range(&self) -> Range {
        Range {
            min: -self.span(),
            max: self.span(),
        }
    }

    /// Returns the range that would be generated if we bounded every value in the other range.
    pub fn bound_range(&self, other: &Range) -> Range {
        // If we overlap, every bounded value will lie in that overlapping.
        if let Some(intersection) = self.intersect(other) {
            intersection
        // Otherwise all values will be projected to a single point, depending on whether the
        // other range lies entirely above or below this range.
        } else if self.min > other.max {
            Range { min: self.min, max: self.min }
        } else {
            Range { min: self.max, max: self.max }
        }
    }

    /// Returns the largest range that is contained by both self and other.
    pub fn intersect(&self, other: &Range) -> Option<Range> {
        let max = std::cmp::min(self.max, other.max);
        let min = std::cmp::max(self.min, other.min);
        if min > max {
            None
        } else {
            Some(Range {min, max})
        }
    }

    /// Returns true if some value lies in both this range and the other.
    pub fn intersects_with(&self, other: &Range) -> bool {
        self.intersect(other).is_some()
    }

    /// Returns the smallest range that contains both self and other.
    /// We don't call this `union` because values that are in neither original range
    /// may show up in the merged range.
    pub fn merge(&self, other: &Range) -> Range {
        let min = std::cmp::min(self.min, other.min);
        let max = std::cmp::max(self.max, other.max);

        Range { min, max }
    }

    /// Returns a range if there is a contiguous range that is the union of both of these.
    /// If such a range does not exist (e.g. there is empty space between them), returns None.
    pub fn try_union(&self, other: &Range) -> Option<Range> {
        if self.intersect(other).is_none() &&
           ! self.max.is_acjadent(other.min) &&
           ! self.min.is_acjadent(other.max)
        {
            return None;
        }

        Some(Range {
            min: std::cmp::min(self.min, other.min),
            max: std::cmp::max(self.max, other.max),
        })
    }

    /// Tests whether this range is a subset of another range.
    pub fn is_subset_of(&self, other: &Range) -> bool {
        self.intersect(other) == Some(*self)
    }

    /// Tests whether these ranges have no overlap.
    pub fn is_disjoint_with(&self, other: &Range) -> bool {
        self.intersect(other).is_none()
    }

    /// Returns whether this range is bounded in a mathematical sense.
    pub fn is_bounded(&self) -> bool {
        self.min > ExtendedInteger::NegativeInfinity && self.max < ExtendedInteger::PositiveInfinity
    }
}

#[test]
fn unittest() {
    // Intersection test
    assert_eq!(
        Range::new(Some(1), Some(3)).intersect(&Range::new(Some(2), Some(4))),
        Some(Range::new(Some(2), Some(3)))
    );
    assert_eq!(
        Range::new(Some(2), Some(4)).intersect(&Range::new(Some(1), Some(3))),
        Some(Range::new(Some(2), Some(3)))
    );
    assert_eq!(
        Range::new(Some(1), Some(3)).intersect(&Range::new(Some(5), Some(7))),
        None,
    );
    assert_eq!(
        Range::new(Some(1), Some(3)).intersect(&Range::new(Some(-4), Some(-2))),
        None,
    );
    assert_eq!(
        Range::new(Some(1), Some(3)).intersect(&Range::new(Some(-4), None)),
        Some(Range::new(Some(1), Some(3))),
    );

    //Bounding tests.
    assert_eq!(
        Range::new(Some(1), Some(3)).bound_range(&Range::new(Some(2), Some(4))),
        Range::new(Some(2), Some(3))
    );
    assert_eq!(
        Range::new(Some(1), Some(3)).bound_range(&Range::new(None, Some(4))),
        Range::new(Some(1), Some(3))
    );
    assert_eq!(
        Range::new(Some(1), None).bound_range(&Range::new(None, Some(4))),
        Range::new(Some(1), Some(4))
    );
    assert_eq!(
        Range::new(None, None).bound_range(&Range::new(None, Some(4))),
        Range::new(None, Some(4))
    );
    assert_eq!(
        Range::new(None, Some(3)).bound_range(&Range::new(Some(5), None)),
        Range::new(Some(3), Some(3))
    );
    assert_eq!(
        Range::new(Some(3), None).bound_range(&Range::new(Some(-2), Some(1))),
        Range::new(Some(3), Some(3))
    );

    // Delta-range tests.
    assert_eq!(
        Range::new(Some(3), Some(7)).delta_range(),
        Range::new(Some(-4), Some(4))
    );
    assert_eq!(
        Range::new(Some(-12), Some(2)).delta_range(),
        Range::new(Some(-14), Some(14))
    );
    assert_eq!(
        Range::new(Some(3), None).delta_range(),
        Range::new(None, None)
    );
    assert_eq!(
        Range::new(None, Some(7)).delta_range(),
        Range::new(None, None)
    );
    assert_eq!(
        Range::new(None, None).delta_range(),
        Range::new(None, None)
    );

    assert_eq!(ExtendedInteger::Discrete(5).mul_f64_round(0.5, f64::floor), ExtendedInteger::Discrete(2));
    assert_eq!(ExtendedInteger::Discrete(-7).mul_f64_round(0.3, f64::floor), ExtendedInteger::Discrete(-3));
    assert_eq!(ExtendedInteger::PositiveInfinity.mul_f64_round(0.3, f64::floor), ExtendedInteger::PositiveInfinity);
    assert_eq!(ExtendedInteger::NegativeInfinity.mul_f64_round(0.3, f64::floor), ExtendedInteger::NegativeInfinity);
    assert_eq!(ExtendedInteger::PositiveInfinity.mul_f64_round(-0.3, f64::floor), ExtendedInteger::NegativeInfinity);
    assert_eq!(ExtendedInteger::NegativeInfinity.mul_f64_round(-0.3, f64::floor), ExtendedInteger::PositiveInfinity);
    
}0707010000004F000081A4000000000000000000000001647DB2300000168D000000000000000000000000000000000000002100000000evsieve-1.4.0~0/src/scancodes.rs// SPDX licence header intentionally missing.
//
// To the extent I own the copyright on this file, it is licensed under "GPL-2.0-or-later".
// However, I am not a lawyer and not certain who owns the "key code -> scancode" table or
// whether it is copyrightable at all. It is probably derived from the USB standard. The USB 
// specification on HID Usage Tables mentions 
//
//     It is contemplated that many implementations of this specification (e.g., in a product)
//     do not require a license to use this specification under copyright. For clarity,
//     however, to the maximum extent of usb implementers forum’s rights, usb
//     implementers forum hereby grants a license under copyright to use this specification
//     as reasonably necessary to implement this specification (e.g., in a product).
//
// The USB specification can be obtained from https://usb.org/document-library/hid-usage-tables-13
// (retrieved April 29th, 2022.)
//
// Since the Linux kernel clearly contains the following table, we can hopefully assume that
// it is compatible with at least "GPL-2.0-only WITH Linux-syscall-note".

use std::collections::HashMap;
use crate::ecodes;
use crate::event::EventCode;

pub type Scancode = i32;

lazy_static! {
    static ref SCANCODES: HashMap<EventCode, Scancode> = {
        // TODO: LOW-PRIORITY: the following table is still incomplete and possibly incorrect.
        let hardcoded_scancodes: &[(&'static str, Scancode)] = &[
            (&"key:a", 458756),
            (&"key:b", 458757),
            (&"key:c", 458758),
            (&"key:d", 458759),
            (&"key:e", 458760),
            (&"key:f", 458761),
            (&"key:g", 458762),
            (&"key:h", 458763),
            (&"key:i", 458764),
            (&"key:j", 458765),
            (&"key:k", 458766),
            (&"key:l", 458767),
            (&"key:m", 458768),
            (&"key:n", 458769),
            (&"key:o", 458770),
            (&"key:p", 458771),
            (&"key:q", 458772),
            (&"key:r", 458773),
            (&"key:s", 458774),
            (&"key:t", 458775),
            (&"key:u", 458776),
            (&"key:v", 458777),
            (&"key:w", 458778),
            (&"key:x", 458779),
            (&"key:y", 458780),
            (&"key:z", 458781),
            (&"key:1", 458782),
            (&"key:2", 458783),
            (&"key:3", 458784),
            (&"key:4", 458785),
            (&"key:5", 458786),
            (&"key:6", 458787),
            (&"key:7", 458788),
            (&"key:8", 458789),
            (&"key:9", 458790),
            (&"key:0", 458791),
            (&"key:enter", 458792),
            (&"key:esc", 458793),
            (&"key:backspace", 458794),
            (&"key:tab", 458795),
            (&"key:space", 458796),
            (&"key:minus", 458797),
            (&"key:equal", 458798),
            (&"key:leftbrace", 458799),
            (&"key:rightbrace", 458800),
            (&"key:backslash", 458801),
            (&"key:semicolon", 458803),
            (&"key:apostrophe", 458804),
            (&"key:grave", 458805),
            (&"key:comma", 458806),
            (&"key:dot", 458807),
            (&"key:slash", 458808),
            (&"key:capslock", 458809),
            (&"key:f1", 458810),
            (&"key:f2", 458811),
            (&"key:f3", 458812),
            (&"key:f4", 458813),
            (&"key:f5", 458814),
            (&"key:f6", 458815),
            (&"key:f7", 458816),
            (&"key:f8", 458817),
            (&"key:f9", 458818),
            (&"key:f10", 458819),
            (&"key:f11", 458820),
            (&"key:f12", 458821),
            (&"key:sysrq", 458822),
            (&"key:scrolllock", 458823),
            (&"key:pause", 458824),
            (&"key:insert", 458825),
            (&"key:home", 458826),
            (&"key:pageup", 458827),
            (&"key:delete", 458828),
            (&"key:end", 458829),
            (&"key:pagedown", 458830),
            (&"key:right", 458831),
            (&"key:left", 458832),
            (&"key:down", 458833),
            (&"key:up", 458834),
            (&"key:numlock", 458835),
            (&"key:kpslash", 458836),
            (&"key:kpasterisk", 458837),
            (&"key:kpminus", 458838),
            (&"key:kpplus", 458839),
            (&"key:kpenter", 458840),
            (&"key:kp1", 458841),
            (&"key:kp2", 458842),
            (&"key:kp3", 458843),
            (&"key:kp4", 458844),
            (&"key:kp5", 458845),
            (&"key:kp6", 458846),
            (&"key:kp7", 458847),
            (&"key:kp8", 458848),
            (&"key:kp9", 458849),
            (&"key:kp0", 458850),
            (&"key:kpdot", 458851),
            (&"key:compose", 458853),
            (&"key:leftctrl", 458976),
            (&"key:leftshift", 458977),
            (&"key:leftalt", 458978),
            (&"key:leftmeta", 458979),
            (&"key:rightctrl", 458980),
            (&"key:rightshift", 458981),
            (&"key:rightalt", 458982),
        ];

        hardcoded_scancodes.into_iter().filter_map(|(key_str, scancode)| {
            let (type_name, code_name_opt) = crate::utils::split_once(key_str, ":");
            let code_name = code_name_opt.unwrap(); // Unwrap ok: data is hardcoded.

            // We defensively check for None here because whether these codes exist might
            // depend on the version of libevdev we link against.
            if let Some(event_code) = ecodes::event_code(type_name, code_name) {
                Some((event_code, scancode.clone()))
            } else {
                None
            }
        }).collect()
    };
}

pub fn from_event_code(code: EventCode) -> Option<Scancode> {
    SCANCODES.get(&code).cloned()
}
07070100000050000081A4000000000000000000000001647DB23000000F18000000000000000000000000000000000000001E00000000evsieve-1.4.0~0/src/signal.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::mem::MaybeUninit;
use std::os::unix::prelude::{AsRawFd, RawFd};

use crate::error::{SystemError, Context};
use crate::io::fd::{HasFixedFd, OwnedFd};

/// As long as a SignalBlock exists, this program will not receive any signals unless it asks
/// for them. Only one SignalBlock should ever exist simultaneously, having more of them is
/// a logical error that can permanently destroy the program's ability to receive signals.
pub struct SignalBlock {
    orig_sigmask: libc::sigset_t,
}

#[repr(transparent)]
pub struct SigMask(libc::sigset_t);

#[allow(clippy::should_implement_trait)]
impl SigMask {
    pub fn new() -> SigMask {
        unsafe {
            let mut sigmask: std::mem::MaybeUninit<libc::sigset_t> = MaybeUninit::zeroed();
            libc::sigemptyset(sigmask.as_mut_ptr());
            SigMask(sigmask.assume_init())
        }
    }

    pub fn as_mut(&mut self) -> &mut libc::sigset_t {
        &mut self.0
    }

    pub fn as_ref(&self) -> &libc::sigset_t {
        &self.0
    }

    pub fn fill(&mut self) -> &mut Self {
        unsafe { libc::sigfillset(self.as_mut()); }
        self
    }

    pub fn add(&mut self, signal: libc::c_int) -> &mut Self {
        unsafe { libc::sigaddset(self.as_mut(), signal); }
        self
    }

    pub fn del(&mut self, signal: libc::c_int) -> &mut Self {
        unsafe { libc::sigdelset(self.as_mut(), signal); }
        self
    }
}

impl SignalBlock {
    /// # Safety
    /// Only one SignalBlock should exist at any time.
    pub unsafe fn new(mask: &SigMask) -> Result<SignalBlock, std::io::Error> {
        let mut orig_sigmask: MaybeUninit<libc::sigset_t> = MaybeUninit::zeroed();
        let res = libc::sigprocmask(libc::SIG_SETMASK, mask.as_ref(), orig_sigmask.as_mut_ptr());
        if res < 0 {
            Err(std::io::Error::last_os_error())
        } else {
            Ok(SignalBlock { orig_sigmask: orig_sigmask.assume_init() })
        }
    }

    pub fn orig_sigmask(&self) -> &libc::sigset_t {
        &self.orig_sigmask
    }
}

impl Drop for SignalBlock {
    fn drop(&mut self) {
        unsafe {
            let orig_sigmask_ptr = &self.orig_sigmask as *const libc::sigset_t;
            libc::sigprocmask(libc::SIG_SETMASK, orig_sigmask_ptr, std::ptr::null_mut());
        }
    }
}

pub type SignalNumber = libc::c_int;

pub struct SignalFd {
    /// The signal fd to communicate with the OS. Beware: SignalFd implements HasFixedFd.
    fd: OwnedFd,
}

impl SignalFd {
    pub fn new(sigmask: &SigMask) -> Result<SignalFd, SystemError> {
        let fd: OwnedFd = unsafe {
            OwnedFd::from_syscall(
                libc::signalfd(-1, sigmask.as_ref(), libc::SFD_NONBLOCK | libc::SFD_CLOEXEC)
            ).with_context("While creating a signal fd:")?
        };
        Ok(SignalFd { fd })
    }

    pub fn read_raw(&mut self) -> Result<libc::signalfd_siginfo, std::io::Error> {
        const SIGNAL_INFO_SIZE: usize = std::mem::size_of::<libc::signalfd_siginfo>();
        let mut signal_info: MaybeUninit<libc::signalfd_siginfo> = MaybeUninit::uninit();
        let result = unsafe { libc::read(self.as_raw_fd(), signal_info.as_mut_ptr() as *mut libc::c_void, SIGNAL_INFO_SIZE) };
        
        if result == SIGNAL_INFO_SIZE as isize {
            Ok(unsafe { signal_info.assume_init() })
        } else if result < 0 {
            Err(std::io::Error::last_os_error())
        } else if result == 0 {
            Err(std::io::Error::new(std::io::ErrorKind::WouldBlock, "Read zero bytes from a signalfd."))
        } else {
            Err(std::io::Error::new(std::io::ErrorKind::Other, "Reading a signalfd returned invalid amount of bytes."))
        }
    }
}

impl AsRawFd for SignalFd {
    fn as_raw_fd(&self) -> RawFd {
        self.fd.as_raw_fd()
    }
}
unsafe impl HasFixedFd for SignalFd {}07070100000051000081A4000000000000000000000001647DB23000000B27000000000000000000000000000000000000001D00000000evsieve-1.4.0~0/src/state.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::ops::{Index,IndexMut};
use std::collections::HashMap;
use crate::error::InternalError;
use crate::event::Channel;

/// Represents the state of the stream that can change as events flow through it.
pub struct State {
    /// Represents the state of --toggle arguments.
    toggles: Vec<ToggleState>,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ToggleIndex(usize);

impl State {
    pub fn new() -> State {
        State {
            toggles: Vec::new(),
        }
    }

    /// Adds a ToggleState to self and returns the index at which it can be accessed.
    pub fn push_toggle(&mut self, value: ToggleState) -> ToggleIndex {
        self.toggles.push(value);
        ToggleIndex(self.toggles.len() - 1)
    }

    /// Returns all toggles except those with a listed index.
    pub fn get_toggles_except<'a>(&'a mut self, excluded_indices: &'a [ToggleIndex]) -> impl Iterator<Item=&'a mut ToggleState> {
        self.toggles.iter_mut().enumerate().filter(
            move |(index, _)| {
                ! excluded_indices.iter().any(|excluded_index| *index == excluded_index.0)
            }
        ).map(|(_, item)| item)
    }

    pub fn create_toggle_with_size(&mut self, size: usize) -> Result<ToggleIndex, InternalError> {
        let toggle_state = ToggleState::new(size)?;
        Ok(self.push_toggle(toggle_state))
    }
}

impl Index<ToggleIndex> for State {
    type Output = ToggleState;
    fn index(&self, index: ToggleIndex) -> &ToggleState {
        &self.toggles[index.0]
    }
}

impl IndexMut<ToggleIndex> for State {
    fn index_mut(&mut self, index: ToggleIndex) -> &mut ToggleState {
        &mut self.toggles[index.0]
    }
}

pub struct ToggleState {
    /// The current output of this toggle that is active.
    /// Note that this value is zero-indexed, although the user-facing interface is one-indexed.
    value: usize,

    /// The amount of states that can be toggled between.
    size: usize,

    /// If the last value of a specific channel was not zero, consistent maps will remember
    /// to which index that event was last routed.
    pub memory: HashMap<Channel, usize>,
}

impl ToggleState {
    pub fn new(size: usize) -> Result<ToggleState, InternalError> {
        if size > 0 {
            Ok(ToggleState { size, value: 0, memory: HashMap::new() })
        } else {
            Err(InternalError::new("A toggle requires at least one state."))
        }
    }

    /// Moves this toggle's active output to the next one.
    pub fn advance(&mut self) {
        self.value += 1;
        self.value %= self.size;
    }

    pub fn value(&self) -> usize {
        self.value
    }

    pub fn set_value_wrapped(&mut self, value: usize) {
        self.value = value % self.size
    }

    pub fn size(&self) -> usize {
        self.size
    }
}07070100000052000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001B00000000evsieve-1.4.0~0/src/stream07070100000053000081A4000000000000000000000001647DB230000030E0000000000000000000000000000000000000001E00000000evsieve-1.4.0~0/src/stream.rs// SPDX-License-Identifier: GPL-2.0-or-later

pub mod print;
pub mod withhold;
pub mod hook;
pub mod map;
pub mod delay;
pub mod merge;

use std::collections::HashMap;

use self::map::{Map, Toggle};
use self::hook::Hook;
use self::print::EventPrinter;
use self::withhold::Withhold;
use self::merge::Merge;

use crate::io::input::InputDevice;
use crate::predevice::PreOutputDevice;
use crate::state::{State, ToggleIndex};
use crate::event::{Event, Namespace};
use crate::capability::{Capability, InputCapabilites};
use crate::io::output::OutputSystem;
use crate::error::RuntimeError;
use crate::loopback::{Loopback, LoopbackHandle, Delay};
use crate::time::Instant;

/// An enum of everything that can be part of the event processing stream.
///
/// There is no formal interface of what these entries need to be capable of, but they need to
/// have rhoughly two functions:
///
/// * `apply_to_all()`, which takes as input a buffer of events, processes them, and then writes
///   them to an output buffer. Events that are left untouched must be written to the output buffer
///   as well, because anything not written to the output buffer is dropped.
/// * `apply_to_all_caps()`, which is like the previous function, but applies to capabilities instead.
///   Given all events (capabilities) that can possibly enter this entry, it must write all
///   events/capabilities that can leave this entry to an output buffer.
/// * `wakeup()`: entries can use the `LoopbackHandle` to request that their `wakeup()` method is
///   called at a laterpoint in time with a certain token. When their `wakeup()` is called, they
///   should check if the token is one of the tokens they scheduled, and if so, do something.
///   It is possible for `wakeup()` to be called with irrelevant tokens, in which case they
///   should do nothing. The `wakeup()` method may output new events for the stream.
///
/// Note that `apply_to_all()` is allowed to take an `&mut self` to change event handling logic at
/// runtime, but it should never modify `self` in a way that the output of `apply_to_all_caps()` changes.
/// The output of `apply_to_all_caps()` must be agnostic of the entry's current runtime state.
pub enum StreamEntry {
    Map(Map),
    Hook(Hook),
    Toggle(Toggle),
    Print(EventPrinter),
    Merge(Merge),
    Withhold(Withhold),
    Delay(self::delay::Delay),
}

pub struct Setup {
    stream: Vec<StreamEntry>,
    output: OutputSystem,
    state: State,
    toggle_indices: HashMap<String, ToggleIndex>,
    loopback: Loopback,
    /// The capabilities all input devices are capable of, and the tentative capabilites of devices that
    /// may be (re)opened in the future. If a new device gets opened, make sure to call `update_caps`
    /// with that device to keep the bookholding straight.
    input_caps: InputCapabilites,
    /// A vector of events that have been "sent" to an output device but are not actually written
    /// to it yet because we await an EV_SYN event.
    staged_events: Vec<Event>,
}

impl Setup {
    pub fn create(
        stream: Vec<StreamEntry>,
        pre_output: Vec<PreOutputDevice>,
        state: State,
        toggle_indices: HashMap<String, ToggleIndex>,
        input_caps: InputCapabilites,
    ) -> Result<Setup, RuntimeError> {
        let caps_vec: Vec<Capability> = crate::capability::input_caps_to_vec(&input_caps);
        let caps_out = run_caps(&stream, caps_vec);
        let output = OutputSystem::create(pre_output, caps_out)?;
        Ok(Setup {
            stream, output, state, toggle_indices, input_caps,
            loopback: Loopback::new(), staged_events: Vec::new(),
        })
    }

    /// Call this function if the capabilities of a certain input device may have changed, e.g. because
    /// it has been reopened after the program started. If the new capabilities are incompatible with
    /// its previous capabilities, then output devices may be recreated.
    pub fn update_caps(&mut self, new_device: &InputDevice) {
        let old_caps_opt = self.input_caps.insert(
            new_device.domain(),
            new_device.capabilities().clone()
        );

        if let Some(old_caps) = old_caps_opt {
            if new_device.capabilities().is_compatible_with(&old_caps) {
                return;
            }
        }

        let caps_vec: Vec<Capability> = crate::capability::input_caps_to_vec(&self.input_caps);
        let caps_out = run_caps(&self.stream, caps_vec);
        self.output.update_caps(caps_out);
    }

    pub fn time_until_next_wakeup(&self) -> Delay {
        self.loopback.time_until_next_wakeup()
    }

    pub fn toggle_indices(&self) -> &HashMap<String, ToggleIndex> {
        &self.toggle_indices
    }

    pub fn state(&self) -> &State {
        &self.state
    }

    pub fn state_mut(&mut self) -> &mut State {
        &mut self.state
    }
}

/// Handles a single event that was generated by an input device. This is the function other
/// modules are supposed to call when they have an input event they want to get handled.
pub fn run(setup: &mut Setup, time: Instant, event: Event) {
    if event.ev_type().is_syn() {
        syn(setup);
    } else {
        // If the auto-scan feature is enabled, MSC_SCAN events will be automatically
        // generated and are therefore blocked just like EV_SYN events are.
        if cfg!(feature = "auto-scan") {
            if event.code == crate::event::EventCode::MSC_SCAN {
                return;
            }
        }

        let mut loopback_handle = setup.loopback.get_handle(time);
        let mut events_out = Vec::new();

        run_events(
            vec![event],
            &mut events_out,
            &mut setup.stream,
            &mut setup.state,
            &mut loopback_handle,
        );

        // If a single event gets mapped to a single event, then the resulting event gets
        // synchronised whenever the input device does. This makes the result of
        //     --input PATH grab --output
        // generate an output device that resembles the input as closely as possible.
        //
        // However, when a single event gets mapped to multiple events, we want to add a
        // SYN event after each event, because otherwise the OS might misorder commands like
        //     --input PATH grab --map key:f12 key:leftctrl key:c --output
        match events_out.len() {
            0 => {},
            1 => setup.staged_events.extend(events_out),
            _ => {
                for event in events_out {
                    setup.staged_events.push(event);
                    syn(setup);
                }
            }
        }
    }
}

/// Runs all events from the loopback device that were due before `now`. If running such an event causes
/// other events to get added that are due before now, then those events get processed as well.
pub fn wakeup_until(setup: &mut Setup, now: Instant) {
    while let Some((instant, token)) = setup.loopback.poll_once(now) {
        let mut loopback_handle = setup.loopback.get_handle(instant);
        run_wakeup(
            token,
            &mut setup.staged_events,
            &mut setup.stream,
            &mut setup.state,
            &mut loopback_handle,
        );
        
        syn(setup);
    };
}

pub fn syn(setup: &mut Setup) {
    setup.output.route_events(&setup.staged_events);
    setup.staged_events.clear();
    setup.output.synchronize();
}

/// Starts processing the stream at a given starting point.
/// 
/// The usual way to call it is by starting with a single input event as events_in and the
/// streamequal to `Setup.stream`, as done by `run()`. However, the starting point can be
/// changed by passing a subslice of `Setup.stream` as the stream. Furthermore, it can start
/// the stream with multiple events in it, though this shouldn't be done for events that were
/// read from actual input devices. This advanced configurability is mainly intended for the
/// `wakeup()` function to be able to pause and resume event processing at a later point in time.
/// 
/// `stream` may be the empty slice.
fn run_events(events_in: Vec<Event>, events_out: &mut Vec<Event>, stream: &mut [StreamEntry], state: &mut State, loopback: &mut LoopbackHandle) {
    let mut events: Vec<Event> = events_in;
    let mut buffer: Vec<Event> = Vec::new();

    for entry in stream {
        match entry {
            StreamEntry::Map(map) => {
                map.apply_to_all(&events, &mut buffer);
                events.clear();
                std::mem::swap(&mut events, &mut buffer);
            },
            StreamEntry::Toggle(toggle) => {
                toggle.apply_to_all(&events, &mut buffer, state);
                events.clear();
                std::mem::swap(&mut events, &mut buffer);
            },
            StreamEntry::Merge(merge) => {
                merge.apply_to_all(&events, &mut buffer);
                events.clear();
                std::mem::swap(&mut events, &mut buffer);
            },
            StreamEntry::Hook(hook) => {
                hook.apply_to_all(&events, &mut buffer, state, loopback);
                events.clear();
                std::mem::swap(&mut events, &mut buffer);
            },
            StreamEntry::Delay(delay) => {
                delay.apply_to_all(&events, &mut buffer, loopback);
                events.clear();
                std::mem::swap(&mut events, &mut buffer);
            },
            StreamEntry::Withhold(withhold) => {
                withhold.apply_to_all(&events, &mut buffer, loopback);
                events.clear();
                std::mem::swap(&mut events, &mut buffer);
            },
            StreamEntry::Print(printer) => {
                printer.apply_to_all(&events);
            },
        }
    }

    events_out.extend(
        events.into_iter().filter(|event| event.namespace == Namespace::Output)
    );
}

fn run_wakeup(token: crate::loopback::Token, events_out: &mut Vec<Event>, stream: &mut [StreamEntry], state: &mut State, loopback: &mut LoopbackHandle) {
    let mut events: Vec<Event> = Vec::new();

    for index in 0 .. stream.len() {
        match &mut stream[index] {
            StreamEntry::Map(_map) => {},
            StreamEntry::Toggle(_toggle) => {},
            StreamEntry::Merge(_merge) => {},
            StreamEntry::Hook(hook) => {
                hook.wakeup(&token);
            },
            StreamEntry::Delay(delay) => {
                delay.wakeup(&token, &mut events);
            },
            StreamEntry::Withhold(withhold) => {
                withhold.wakeup(&token, &mut events);
            },
            StreamEntry::Print(_printer) => {},
        }

        if ! events.is_empty() {
            // If index+1 == stream.len(), then stream[index+1..] is the empty slice.
            run_events(events, events_out, &mut stream[index+1..], state, loopback);
            events = Vec::new();
        }
    }
}

/// A direct analogue for run_once(), except it runs through capabilities instead of events.
pub fn run_caps(stream: &[StreamEntry], capabilities: Vec<Capability>) -> Vec<Capability> {
    let mut caps: Vec<Capability> = capabilities;
    let mut buffer: Vec<Capability> = Vec::new();
    let mut last_num_caps = caps.len();
    
    for entry in stream {
        match entry {
            StreamEntry::Map(map) => {
                map.apply_to_all_caps(&caps, &mut buffer);
                caps.clear();
                std::mem::swap(&mut caps, &mut buffer);
            },
            StreamEntry::Toggle(toggle) => {
                toggle.apply_to_all_caps(&caps, &mut buffer);
                caps.clear();
                std::mem::swap(&mut caps, &mut buffer);
            },
            StreamEntry::Merge(_) => (),
            StreamEntry::Hook(hook) => {
                hook.apply_to_all_caps(&caps, &mut buffer);
                caps.clear();
                std::mem::swap(&mut caps, &mut buffer);
            },
            StreamEntry::Print(_) => (),
            StreamEntry::Delay(_) => (),
            StreamEntry::Withhold(_) => (),
        }

        // Merge capabilities that differ only in value together when possible.
        // This avoids a worst-case scenario with exponential computation time.
        if caps.len() >= 2 * last_num_caps {
            caps = crate::capability::aggregate_capabilities(caps);
            last_num_caps = caps.len();
        }
    }

    caps.into_iter().filter(|cap| cap.namespace == Namespace::Output).collect()
}07070100000054000081A4000000000000000000000001647DB23000000774000000000000000000000000000000000000002400000000evsieve-1.4.0~0/src/stream/delay.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::loopback::{LoopbackHandle, Token};
use crate::event::Event;
use crate::key::Key;
use crate::time::Duration;

/// All events that reach the delay shall be removed and put back into the stream after 
/// a certain amount of time passes.
pub struct Delay {
    keys: Vec<Key>,
    period: Duration,

    /// State: modifiable at runtime.
    /// Events that need to be put back into thes stream when the loopback releases a certain token.
    delayed_events: Vec<(Token, Vec<Event>)>,
}

impl Delay {
    pub fn new(keys: Vec<Key>, period: Duration) -> Delay {
        Delay {
            keys, period,
            delayed_events: Vec::new(),
        }
    }

    /// Checks if some events matches this delay's keys, and if so, withholds them for a
    /// specified period.
    pub fn apply_to_all(&mut self, events: &[Event], output_events: &mut Vec<Event>, loopback: &mut LoopbackHandle) {
        let mut events_to_withhold: Vec<Event> = Vec::new();
        for &event in events {
            if self.keys.iter().any(|key| key.matches(&event)) {
                events_to_withhold.push(event);
            } else {
                output_events.push(event);
            }
        }

        if ! events_to_withhold.is_empty() {
            let wakeup_token = loopback.schedule_wakeup_in(self.period);
            self.delayed_events.push((wakeup_token, events_to_withhold));
        }
    }

    /// All delayed events that are overdue will be put back into the stream.
    pub fn wakeup(&mut self, token: &Token, output_events: &mut Vec<Event>) {
        while let Some((wakeup_token, delayed_events)) = self.delayed_events.first() {
            if wakeup_token == token {
                output_events.extend(delayed_events);
                self.delayed_events.remove(0);
            } else {
                break;
            }
        }
    }
}07070100000055000081A4000000000000000000000001647DB230000044FA000000000000000000000000000000000000002300000000evsieve-1.4.0~0/src/stream/hook.rsuse crate::error::Context;
use crate::range::Range;
use crate::key::Key;
use crate::event::{Event, Channel};
use crate::state::State;
use crate::subprocess;
use crate::loopback;
use crate::loopback::LoopbackHandle;
use crate::capability::{Capability, CapMatch};
use crate::time::Duration;
use std::collections::HashSet;

// TODO: HIGH-PRIORITY Check whether the ordering behaviour of --withhold is consistent
// with --hook send-key.

pub type Effect = Box<dyn Fn(&mut State)>;

/// Represents the point at time after which a pressed tracker is no longer valid.
/// Usually determined by the --hook period= clause.
pub enum ExpirationTime {
    Never,
    Until(loopback::Token),
}

enum TrackerState {
    /// This tracker's corresponding key is held down. Also keeps track of how much time is
    /// left until this tracker expires due to a period= clause. If no period= clause was
    /// specified, then its expiration time shall be ExpirationTime::Never.
    Active(ExpirationTime),
    /// This tracker's corresponding key is not held down.
    Inactive,
    /// Based on the events that were received by this Tracker, the state should be active,
    /// but it is counted as inactive due to some circumstances, e.g. because the period
    /// in which the hook must be triggered expired, or because this tracker was activated
    /// before its predecessors in a sequential hook.
    /// 
    /// To activate this tracker, it first needs to return to Inactive and then activate.
    Invalid,
}

impl TrackerState {
    fn is_active(&self) -> bool {
        match self {
            TrackerState::Active (_) => true,
            TrackerState::Inactive | TrackerState::Invalid => false,
        }
    }
}

/// A tracker is used to track whether a certain key is held down. This is useful for --hook type
/// arguments.
struct Tracker {
    key: Key,
    range: Range,

    /// The state is mutable at runtime. It reflects whether the key tracked by this tracker
    /// is currently pressed or not, as well as which event triggered it and when.
    state: TrackerState,
}

impl Tracker {
    fn new(mut key: Key) -> Tracker {
        let range = key.pop_value().unwrap_or_else(|| Range::new(Some(1), None));
        Tracker {
            key,
            range,
            state: TrackerState::Inactive,
        }
    }

    /// Returns true if this event might interact with this tracker in some way.
    fn matches(&self, event: &Event) -> bool {
        self.key.matches(event)
    }

    /// Returns true if any event with the given channel might interact with this
    /// tracker in some way.
    fn matches_channel(&self, channel: Channel) -> bool {
        self.key.matches_channel(channel)
    }

    /// Returns whether this event would turn this tracker on or off.
    /// Only returns sensible values if self.matches(event) is true.
    fn activates_by(&self, event: Event) -> bool {
        self.range.contains(event.value)
    }

    fn is_active(&self) -> bool {
        match self.state {
            TrackerState::Active(_) => true,
            TrackerState::Invalid | TrackerState::Inactive => false,
        }
    }

    /// Like Clone::clone, but does not clone the runtime state of the Tracker.
    fn clone_empty(&self) -> Tracker {
        Tracker {
            key: self.key.clone(),
            range: self.range,
            state: TrackerState::Inactive,
        }
    }
}

/// The Trigger is the inner part of the hook that keeps track of when the hook is supposed to
/// activate.
pub struct Trigger {
    /// If Some, then all trackers must be activated within a certain duration from the first
    /// tracker to activate in order to trigger the hook.
    period: Option<Duration>,
    /// If true, then all trackers belonging to this Trigger must be triggered in sequential
    /// order. If a tracker is activated while its previous tracker is still inactive, then
    /// that tracker becomes invalid.
    sequential: bool,
    breaks_on: Vec<Key>,

    trackers: Vec<Tracker>,
    state: TriggerState,
}

/// Returned by Trigger::apply to inform the caller what effect the provided event had on
/// the hook.
#[derive(Clone, Copy)]
pub enum TriggerResponse {
    /// This event does not interact with this hook in any way.
    None,
    /// This event may have changed the state of this trigger some way. Does not guarantee that
    /// this event actually matches one of its keys or that the state was actually changed.
    /// Guarantees that the trigger was not activated or released.
    Interacts,
    /// The hook has activated because of this event. Its effects should be triggered.
    Activates,
    /// The hook has released because of this event. Its on-release effects should be triggered.
    Releases,
}

#[derive(Clone, Copy)]
enum TriggerState {
    /// All trackers are currently pressed.
    Active,
    /// Not all trackers are currently pressed.
    Inactive,
}

impl Trigger {
    pub fn new(keys: Vec<Key>, breaks_on : Vec<Key>, period: Option<Duration>, sequential: bool) -> Trigger {
        let trackers = keys.into_iter().map(Tracker::new).collect();
        Trigger {
            period, trackers, sequential, breaks_on,
            state: TriggerState::Inactive,
        }
    }

    pub fn apply(&mut self, event: Event, loopback: &mut LoopbackHandle) -> TriggerResponse {
        let mut any_tracker_matched: bool = false;

        for tracker in self.trackers.iter_mut()
            .filter(|tracker| tracker.matches(&event))
        {
            any_tracker_matched = true;

            if tracker.activates_by(event) {
                match tracker.state {
                    // If this tracker was inactive, activate it.
                    TrackerState::Inactive => {
                        // Note: if this hook is sequential, this activation may get invalidated
                        // later in this function.
                        tracker.state = TrackerState::Active(
                            acquire_expiration_token(self.period, loopback)
                        );
                    },
                    TrackerState::Active(..) | TrackerState::Invalid => {},
                }
            } else {
                tracker.state = TrackerState::Inactive;
            };
        }
        
        if ! any_tracker_matched {
            // If none of the trackers match this event, but it does match one of the breaks-on
            // notes, then invalidate all trackers.
            if self.breaks_on.iter().any(|key| key.matches(&event)) {
                let mut any_tracker_invalidated = false;

                for tracker in &mut self.trackers {
                    match tracker.state {
                        TrackerState::Active(_) => {
                            tracker.state = TrackerState::Invalid;
                            any_tracker_invalidated = true;
                            // TODO: LOW-PRIORITY Cancel token.
                        },
                        TrackerState::Inactive | TrackerState::Invalid => {},
                    }
                }

                if ! any_tracker_invalidated {
                    return TriggerResponse::None;
                }
            } else {
                // No trackers care about this event.
                return TriggerResponse::None;
            }
        }

        if self.sequential {
            // Invalidate all trackers that activated out of order.
            self.trackers.iter_mut()
                // Skip all trackers that are consecutively active from the start.
                .skip_while(|tracker| tracker.is_active())
                // ... then find all trackers that are active but not consecutively so.
                .filter(|tracker| tracker.is_active())
                // ... and invalidate them.
                // TODO: LOW-PRIORITY Consider canceling the activation token.
                .for_each(|tracker| tracker.state = TrackerState::Invalid);
        }

        // Check if we transitioned between active and inactive.
        let all_trackers_active = self.trackers.iter().all(|tracker| tracker.state.is_active());

        match (self.state, all_trackers_active) {
            (TriggerState::Inactive, true) => {
                self.state = TriggerState::Active;
                // TODO: LOW-PRIORITY Cancel tokens?
                for tracker in &mut self.trackers {
                    tracker.state = TrackerState::Active(ExpirationTime::Never);
                }
                TriggerResponse::Activates
            },
            (TriggerState::Active, false) => {
                self.state = TriggerState::Inactive;
                TriggerResponse::Releases
            },
            (TriggerState::Active {..}, true) | (TriggerState::Inactive, false)
                => TriggerResponse::Interacts,
        }
    }

    /// Release a tracker that has expired. If a tracker expired, returns the associated key.
    /// It is important that the Tokens are unique for this function to work correctly.
    /// 
    /// Returns true if at least one tracker expired. Returns false otherwise.
    pub fn wakeup(&mut self, token: &loopback::Token) -> bool {
        let mut result = false;
        for tracker in &mut self.trackers {
            match tracker.state {
                TrackerState::Inactive => {},
                TrackerState::Invalid => {},
                TrackerState::Active(ExpirationTime::Never) => {},
                TrackerState::Active(ExpirationTime::Until(ref other_token)) => {
                    if token == other_token {
                        // This tracker expired.
                        tracker.state = TrackerState::Invalid;
                        result = true;
                    }
                }
            }
        }
        result
    }

    /// Returns true if any of the active trackers might have been activated by an event
    /// with the provided channel, regardless of whether that channel actually activated them.
    pub fn has_active_tracker_matching_channel(&self, channel: Channel) -> bool {
        self.trackers.iter()
            .filter(|tracker| tracker.is_active())
            .any(   |tracker| tracker.matches_channel(channel))
    }

    /// Returns true if any of the might be activated by an event with the provided channel.
    pub fn has_tracker_matching_channel(&self, channel: Channel) -> bool {
        self.trackers.iter()
            .any(|tracker| tracker.matches_channel(channel))
    }

    /// Like Clone::clone, but does not clone the runtime state of the Trigger.
    pub fn clone_empty(&self) -> Trigger {
        Trigger {
            sequential: self.sequential,
            period: self.period,
            breaks_on: self.breaks_on.clone(),
            trackers: self.trackers.iter().map(Tracker::clone_empty).collect(),
            state: TriggerState::Inactive,
        }
    }
}

pub struct Hook {
    /// Effects that shall be triggered if this hook activates, i.e. all keys are held down simultaneously.
    effects: Vec<Effect>,
    /// Effects that shall be released after one of the keys has been released after activating.
    release_effects: Vec<Effect>,

    /// The current state mutable at runtime.
    trigger: Trigger,

    /// The substructure responsible for generating additinal events for the send-key clause.
    event_dispatcher: EventDispatcher,
}

impl Hook {
    pub fn new(trigger: Trigger, event_dispatcher: EventDispatcher) -> Hook {
        Hook {
            trigger,
            effects: Vec::new(),
            release_effects: Vec::new(),
            event_dispatcher,
        }
    }

    fn apply(&mut self, event: Event, events_out: &mut Vec<Event>, state: &mut State, loopback: &mut LoopbackHandle) {
        let response = self.trigger.apply(event, loopback);

        self.event_dispatcher.map_event(event, response, events_out);

        match response {
            TriggerResponse::Activates => {
                self.apply_effects(state);
            },
            TriggerResponse::Releases => {
                self.apply_release_effects(state);
            },
            TriggerResponse::Interacts | TriggerResponse::None => (),
        }
    }

    pub fn apply_to_all(
        &mut self,
        events: &[Event],
        events_out: &mut Vec<Event>,
        state: &mut State,
        loopback: &mut LoopbackHandle,
    ) {
        for event in events {
            self.apply(*event, events_out, state, loopback);
        }
    }

    pub fn apply_to_all_caps(
        &self,
        caps: &[Capability],
        caps_out: &mut Vec<Capability>,
    ) {
        caps_out.extend(caps);
        self.event_dispatcher.generate_additional_caps(&self.trigger, caps, caps_out);
    }

    pub fn wakeup(&mut self, token: &loopback::Token) {
        self.trigger.wakeup(token);
    }

    /// Runs all effects that should be ran when this hook triggers.
    fn apply_effects(&self, state: &mut State) {
        for effect in &self.effects {
            effect(state);
        }
    }

    /// Runs all effects that should be ran when this hook has triggered and
    /// a tracked key is released.
    fn apply_release_effects(&self, state: &mut State)
    {
        for release_effect in &self.release_effects {
            release_effect(state);
        }
    }

    /// Makes this hook run an effect when it triggers.
    pub fn add_effect(&mut self, effect: Effect) {
        self.effects.push(effect);
    }

    /// Makes this hook invoke an external subprocess when this hook is triggered.
    pub fn add_command(&mut self, program: String, args: Vec<String>) {
        self.add_effect(
            Box::new(move |_| {
                subprocess::try_spawn(program.clone(), args.clone()).print_err();
            })
        );
    }
}

/// The part of the --hook that is responsible for handling the send-key= clause.
/// Implemented separately from the hook because it is possible we want to remove this
/// functionality from the --hook itself and move it to a --withhold instead.
pub struct EventDispatcher {
    /// Keys that shall be sent on press and release.
    send_keys: Vec<Key>,
    /// The last event that activated the corresponding Hook/Trigger.
    activating_event: Option<Event>,
}

impl EventDispatcher {
    pub fn from_send_keys(send_keys: Vec<Key>) -> EventDispatcher {
        EventDispatcher {
            send_keys,
            activating_event: None
        }
    }

    /// Similar in purpose to apply().
    fn map_event(&mut self, event: Event, trigger_response: TriggerResponse, events_out: &mut Vec<Event>) {
        match trigger_response {
            TriggerResponse::Activates => {
                events_out.push(event);
                self.activating_event = Some(event);
                for key in &self.send_keys {
                    let mut additional_event = key.merge(event);
                    additional_event.value = 1;
                    events_out.push(additional_event);
                };
            },
            TriggerResponse::Releases => {
                let activating_event = match self.activating_event {
                    Some(activating_event) => activating_event,
                    None => {
                        crate::utils::warn_once("Internal error: a hook released without record of being activated by any event. This is a bug.");
                        event
                    }
                };
                for key in self.send_keys.iter().rev() {
                    let mut additional_event = key.merge(activating_event);
                    additional_event.value = 0;
                    events_out.push(additional_event);
                }
                events_out.push(event);
            },
            TriggerResponse::Interacts | TriggerResponse::None => {
                events_out.push(event);
            },
        }
    }

    /// Computes additional capabilities that can be generated by the send_keys and writes them
    /// to caps_out. This function does not add the base capabilities to the output.
    /// 
    /// Similar in purpose to apply_to_all_caps(), but does not copy the base capabilities.
    fn generate_additional_caps(&self, trigger: &Trigger, caps: &[Capability], caps_out: &mut Vec<Capability>) {
        // TODO: LOW-PRIORITY Fix encapsulation?
        let keys: Vec<&Key> = trigger.trackers.iter().map(|tracker| &tracker.key).collect();
        let mut additional_caps: HashSet<Capability> = HashSet::new();
        // TODO: MEDIUM-PRIORITY reduce this implementation to a special case of Map.

        for cap_in in caps {
            let matches_cap = keys.iter()
                .map(|key| key.matches_cap(cap_in)).max();
            match matches_cap {
                Some(CapMatch::Yes | CapMatch::Maybe) => {},
                Some(CapMatch::No) | None => continue,
            };

            additional_caps.extend(self.send_keys.iter().map(
                |key| {
                    let mut new_cap = key.merge_cap(*cap_in);
                    new_cap.value_range = Range::new(Some(0), Some(1));
                    new_cap
                }
            ));
        }

        caps_out.extend(additional_caps);
    }
}

/// If this hook has a period set, acquires a Token from the loopback and arranges for a
/// `wakeup()` call later. If no period is set, return `ExpirationTime::Never`.
fn acquire_expiration_token(period: Option<Duration>, loopback: &mut LoopbackHandle) -> ExpirationTime {
    match period {
        Some(duration) => ExpirationTime::Until(loopback.schedule_wakeup_in(duration)),
        None => ExpirationTime::Never,
    }
}
07070100000056000081A4000000000000000000000001647DB23000001AEA000000000000000000000000000000000000002200000000evsieve-1.4.0~0/src/stream/map.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::key::Key;
use crate::event::{Event, Namespace};
use crate::domain::Domain;
use crate::capability::{Capability, CapMatch};
use crate::error::InternalError;
use crate::state::{State, ToggleIndex};

#[derive(Clone, Debug)]
pub struct Map {
    input_key: Key,
    output_keys: Vec<Key>,
}

impl Map {
    pub fn new(input_key: Key, output_keys: Vec<Key>) -> Map {
        Map { input_key, output_keys }
    }

    /// Returns a map that blocks a given input key.
    pub fn block(input_key: Key) -> Map {
        Map::new(input_key, Vec::new())
    }

    pub fn domain_shift(
            source_domain: Domain, source_namespace: Namespace,
            target_domain: Domain, target_namespace: Namespace
    ) -> Map {
        Map::new(
            Key::from_domain_and_namespace(source_domain, source_namespace),
            vec![Key::from_domain_and_namespace(target_domain, target_namespace)]
        )
    }

    /// Checks if an event matches this map, and if so, generates corresponding events and
    /// writes those to the output. Otherwise, writes the event itself to the output.
    fn apply(&self, event: Event, output_events: &mut Vec<Event>) {
        if ! self.input_key.matches(&event) {
            output_events.push(event);
            return;
        }
        let generated_events = self.output_keys.iter().map(
            |key| key.merge(event)
        );
        output_events.extend(generated_events);
    }

    /// Maps all events to output_events. Events that do not match this Map are mapped to themselfe.
    /// Preserves the order of the events.
    pub fn apply_to_all(&self, events: &[Event], output_events: &mut Vec<Event>) {
        for &event in events {
            self.apply(event, output_events);
        }
    }

    /// An analogue for apply() but with capabilities instead of events.
    fn apply_cap(&self, cap: Capability, output_caps: &mut Vec<Capability>) {
        let matches_cap = self.input_key.matches_cap(&cap);

        // An iterator of the caps we would add if we matched. Do not actually add them yet.
        let generated_caps = self.output_keys.iter().map(
            |key| key.merge_cap(cap)
        );
        
        // Depending on whether or not we match, we should add the generated capabilities
        // and preserve/remove self from the stream.
        match matches_cap {
            CapMatch::Yes => {
                output_caps.extend(generated_caps);
            },
            CapMatch::Maybe => {
                output_caps.push(cap);
                output_caps.extend(generated_caps);
            },
            CapMatch::No => {
                output_caps.push(cap);
            },
        }
    }

    /// Like apply_to_all(), but for capabilities.
    pub fn apply_to_all_caps(&self, caps: &[Capability], output_caps: &mut Vec<Capability>) {
        for &cap in caps {
            self.apply_cap(cap, output_caps);
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ToggleMode {
    Passive,
    Consistent,
}

/// Represents a --toggle argument.
pub struct Toggle {
    input_key: Key,
    output_keys: Vec<Key>,
    pub mode: ToggleMode,
    pub state_index: ToggleIndex,
}

impl Toggle {
    /// Requires at least one output key.
    /// If a predetermined index is supplied, this state will toggle along with that index.
    /// Otherwise, a new index will be created.
    pub fn new(input_key: Key, output_keys: Vec<Key>, mode: ToggleMode,
            state: &mut State, predetermined_index: Option<ToggleIndex>) -> Result<Toggle, InternalError> {
        let num_output = output_keys.len();
        let state_index = match predetermined_index {
            Some(index) => {
                if state[index].size() == num_output {
                    index
                } else {
                    return Err(InternalError::new("The toggle's index size does not match up with the toggle."))
                }
            },
            None => {
                state.create_toggle_with_size(num_output)?
            },
        };
        
        Ok(Toggle { input_key, output_keys, mode, state_index })
    }

    /// Returns the active output key. Specific events may use a different active output key
    /// than this one. Use active_output_key_for_event() instead.
    fn active_output_key(&self, state: &State) -> &Key {
        &self.output_keys[state[self.state_index].value()]
    }

    /// Returns the currently active key for a specific event.
    /// Identical to active_output_key() for passive maps.
    fn active_output_key_for_event(&self, event: Event, state: &State) -> &Key {
        match self.mode {
            ToggleMode::Passive => self.active_output_key(state),
            ToggleMode::Consistent => {
                match state[self.state_index].memory.get(&event.channel()) {
                    Some(&index) => &self.output_keys[index],
                    None => self.active_output_key(state),
                }
            }
        }
    }

    /// If this is a consistent map, keeps track of which events were last routed where,
    /// to ensure that a key_up event is sent to the same target as a key_down event even
    /// if the active map was toggled in the meantime.
    /// 
    /// This should be called _after_ active_output_key_for_event(), because otherwise it
    /// may erase the memory we were left by the previous event.
    fn remember(&self, event: Event, state: &mut State) {
        if self.mode == ToggleMode::Consistent && event.ev_type().is_key() && self.input_key.matches(&event) {
            let active_value = state[self.state_index].value();
            let memory = &mut state[self.state_index].memory;
            let event_channel = event.channel();
            match event.value {
                0 => { memory.remove(&event_channel); },
                _ => { memory.entry(event_channel).or_insert(active_value); },
            }
        }
    }

    fn apply(&self, event: Event, output_events: &mut Vec<Event>, state: &mut State) {
        if self.input_key.matches(&event) {
            let active_output = self.active_output_key_for_event(event, state);
            self.remember(event, state);
            output_events.push(active_output.merge(event));
        } else {
            output_events.push(event);
        }
    }

    /// The apply_ functions are analogous to the Map::apply_ equivalents.
    pub fn apply_to_all(&self, events: &[Event], output_events: &mut Vec<Event>, state: &mut State) {
        for &event in events {
            self.apply(event, output_events, state);
        }
    }

    pub fn apply_to_all_caps(&self, caps: &[Capability], output_caps: &mut Vec<Capability>) {
        let self_as_map = Map::new(self.input_key.clone(), self.output_keys.clone());
        self_as_map.apply_to_all_caps(caps, output_caps);
    }
}
07070100000057000081A4000000000000000000000001647DB2300000087F000000000000000000000000000000000000002400000000evsieve-1.4.0~0/src/stream/merge.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::collections::HashMap;

use crate::key::Key;
use crate::event::{Event, Channel};

/// Represents a --merge argument.
pub struct Merge {
    /// The keys that are subject to getting merged by this argument.
    keys: Vec<Key>,
    
    /// How many down events each (type, code, domain) pair has.
    state: HashMap<Channel, usize>,
}

impl Merge {
    pub fn new(keys: Vec<Key>) -> Merge {
        Merge { keys, state: HashMap::new() }
    }

    #[allow(clippy::needless_return)]
    fn apply(&mut self, event: Event, output_events: &mut Vec<Event>) {
        // If this merge is not applicable to this event, silently pass it on.
        if ! event.ev_type().is_key() || ! self.keys.iter().any(|key| key.matches(&event)) {
            output_events.push(event);
            return;
        }

        let current_down_count: &mut usize = self.state.entry(event.channel()).or_insert(0);
        let last_down_count: usize = *current_down_count;
        match event.value {
            // If this is a KEY_DOWN (1) event, add one to the down count.
            1 => *current_down_count += 1,
            // If this is a KEY_UP (0) event, substract one from the down count, but never go below zero.
            0 => *current_down_count = current_down_count.saturating_sub(1),
            // Otherwise, silently pass on and ignore this event.
            _ => {
                output_events.push(event);
                return;
            },
        }

        match (last_down_count, event.value) {
            // If a KEY_UP event let to the down count becoming zero, or a KEY_DOWN event let to the
            // count becoming one, write it to the output. Importantly, do not pass the event on in
            // case a KEY_UP event resulted into the event staying zero (last: 0, current: 0).
            (1, 0) | (0, 1) => output_events.push(event),
            // Otherwise, drop this event.
            _ => return,
        }
    }

    pub fn apply_to_all(&mut self, events: &[Event], output_events: &mut Vec<Event>) {
        for &event in events {
            self.apply(event, output_events);
        }
    }
}
07070100000058000081A4000000000000000000000001647DB23000000761000000000000000000000000000000000000002400000000evsieve-1.4.0~0/src/stream/print.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::key::Key;
use crate::event::{Event, EventType};
use crate::ecodes;
use crate::domain;

pub enum EventPrintMode {
    Detailed,
    Direct,
}

/// Created by --print arguments.
pub struct EventPrinter {
    keys: Vec<Key>,
    mode: EventPrintMode,
}

impl EventPrinter {
    pub fn new(keys: Vec<Key>, mode: EventPrintMode) -> EventPrinter {
        EventPrinter { keys, mode }
    }

    fn apply(&self, event: Event) {
        if self.keys.iter().any(|key| key.matches(&event)) {
            println!("{}", match self.mode {
                EventPrintMode::Direct => print_event_direct(event),
                EventPrintMode::Detailed => print_event_detailed(event),
            });
        }
    }

    pub fn apply_to_all(&self, events: &[Event]) {
        for &event in events {
            self.apply(event);
        }
    }
}

pub fn print_event_detailed(event: Event) -> String {
    let name = ecodes::event_name(event.code);
    let value_str = match event.ev_type() {
        EventType::KEY => match event.value {
            0 => "0 (up)".to_string(),
            1 => "1 (down)".to_string(),
            2 => "2 (repeat)".to_string(),
            _ => format!("{}", event.value),
        },
        _ => format!("{}", event.value),
    };
    let name_and_value = format!("Event:  type:code = {:<13}  value = {}", name, value_str);

    if let Some(domain_name) = domain::try_reverse_resolve(event.domain) {
        format!("{:<53}  domain = {}", name_and_value, domain_name)
    } else {
        name_and_value
    }
}

pub fn print_event_direct(event: Event) -> String {
    let name = ecodes::event_name(event.code);
    if let Some(domain_name) = domain::try_reverse_resolve(event.domain) {
        format!("{}:{}@{}", name, event.value, domain_name)
    } else {
        format!("{}:{}", name, event.value)
    }
}07070100000059000081A4000000000000000000000001647DB2300000224D000000000000000000000000000000000000002700000000evsieve-1.4.0~0/src/stream/withhold.rs// SPDX-License-Identifier: GPL-2.0-or-later

use crate::event::{Event, Channel};
use crate::key::Key;
use crate::loopback::{LoopbackHandle, Token};
use crate::stream::hook::{Trigger, TriggerResponse};

/// Represents a --withhold argument.
pub struct Withhold {
    /// Copies of the triggers of the associated hooks.
    triggers: Vec<Trigger>,

    /// Only withhold events that match one of the following keys.
    keys: Vec<Key>,

    channel_state: Vec<(Channel, ChannelState)>,
}

impl Withhold {
    pub fn new(keys: Vec<Key>, triggers: Vec<Trigger>) -> Withhold {
        Withhold {
            keys, triggers,
            channel_state: Vec::new(),
        }
    }

    pub fn apply_to_all(&mut self, events: &[Event], events_out: &mut Vec<Event>, loopback: &mut LoopbackHandle) {
        for event in events {
            self.apply(*event, events_out, loopback);
        }
    }

    fn apply(&mut self, event: Event, events_out: &mut Vec<Event>, loopback: &mut LoopbackHandle) {
        // Check which triggers just activated because of this event.
        let mut activated_triggers: Vec<&Trigger> = Vec::new();
        let mut any_tracker_active_on_channel: bool = false;
        let mut any_tracker_interacts: bool = false;
        for trigger in &mut self.triggers {
            match trigger.apply(event, loopback) {
                TriggerResponse::None => {},
                TriggerResponse::Interacts
                | TriggerResponse::Releases => {
                    any_tracker_interacts = true;
                },
                TriggerResponse::Activates => {
                    activated_triggers.push(trigger);
                    any_tracker_interacts = true;
                },
            }
            // TODO: MEDIUM-PRIORITY maybe this information should be returned by trigger.apply()?
            if trigger.has_active_tracker_matching_channel(event.channel()) {
                any_tracker_active_on_channel = true;
            }
        }

        // Skip all events that did not match any preceding hook.
        if ! any_tracker_interacts {
            return events_out.push(event);
        }

        // If this is set to Some, then the provided event shall be added to events_out at the
        // end of the function, i.e. after all other withheld events have been released.
        //
        // Setting this to Some(event) is pretty much a delayed `events_out.push(event)` call.
        let final_event: Option<Event>;

        if self.keys.iter().any(|key| key.matches(&event)) {
            // Decide whether or not to hold this event.

            let current_channel_state: Option<&mut ChannelState> =
                self.channel_state.iter_mut()
                .find(|(channel, _state)| *channel == event.channel())
                .map(|(_channel, state)| state);

            if any_tracker_active_on_channel {
                // If the event value were zero, then the constraint of "no custom value declarations"
                // for the preceding hooks should have made sure that no trackers are active on this
                // channel because a value-zero event would deactivate all of them.
                debug_assert!(event.value != 0);

                if event.value == 1 {
                    // Withhold the event unless an event was already being withheld.
                    match current_channel_state {
                        None => self.channel_state.push(
                            (event.channel(), ChannelState::Withheld { withheld_event: event })
                        ),
                        Some(state @ &mut ChannelState::Residual) => {
                            *state = ChannelState::Withheld { withheld_event: event }
                        },
                        Some(ChannelState::Withheld { .. }) => {},
                    }
                    final_event = None;
                } else {
                    // Drop all repeat events on channels that have an active tracker.
                    final_event = None;
                }
            } else { // No trackers active at the event's channel.
                if event.value == 0 {
                    // Due to the restrictions on the hooks (i.e. only default values), an event of
                    // value zero cannot possibly contribute to activating any hook, so we are free
                    // to pass on this event unless a residual state instructs us to drop this event.

                    match current_channel_state {
                        None | Some(ChannelState::Withheld { .. }) => {
                            // The withheld event will be released by a later piece of code.
                            final_event = Some(event);
                        },
                        Some(ChannelState::Residual) => {
                            // Drop this event and clear the residual state.
                            self.channel_state.retain(|(channel, _)| *channel != event.channel());
                            final_event = None;
                        }
                    }
                } else {
                    // In this case, all corresponding trackers are probably in invalid state.
                    // Anyway, knowing that no trackers are active means that this event won't
                    // contribute to activating a hook, and its value being nonzero means that
                    // we don't have to deal with the residual rules, so we can pass this event
                    // on.
                    final_event = Some(event);
                }
            }
        } else {
            // This event can not be withheld. Add it to the stream after releasing past events.
            final_event = Some(event);
        }

        // All events which were withheld by a trigger that just activated shall be considered
        // to have been consumed and their states are to be set to Residual.
        for (channel, state) in &mut self.channel_state {
            if let ChannelState::Withheld { .. } = state {
                for trigger in &activated_triggers {
                    if trigger.has_tracker_matching_channel(*channel) {
                        *state = ChannelState::Residual;
                        break;
                    }
                }
            }
        }

        // All events which are no longer withheld by any trigger shall be released.
        self.release_events(events_out);

        if let Some(event) = final_event {
            events_out.push(event);
        }
    }

    pub fn wakeup(&mut self, token: &Token, events_out: &mut Vec<Event>) {
        let mut some_tracker_expired = false;
        for trigger in &mut self.triggers {
            if trigger.wakeup(token) {
                some_tracker_expired = true;
            }
        }
        if ! some_tracker_expired {
            return;
        }

        // Some trackers have expired. For all events that are being withheld, check
        // whether the respective triggers are still withholding them. Events that
        // are no longer withheld by any trigger shall be released bach to the stream.
        self.release_events(events_out);
    }

    /// Writes all events that are not withheld by any trigger to the output stream.
    fn release_events(&mut self, events_out: &mut Vec<Event>) {
        let triggers = &self.triggers;
        self.channel_state.retain(|(channel, state)| {
            if let ChannelState::Withheld { withheld_event } = state {
                let is_still_withheld = triggers.iter().any(|trigger|
                    trigger.has_active_tracker_matching_channel(*channel)
                );
                if ! is_still_withheld {
                    events_out.push(*withheld_event);
                    return false;
                }
            }
            true
        });
    }
}

/// For each channel, at most one event can be withheld. This withheld event is always a
/// KEY_DOWN event. Subsequent KEY_DOWN events that arrive while an event is being withheld
/// shall be dropped. The event is withheld as long as some tracker returns true for
/// `has_active_tracker_matching_channel(event.channel())`.
/// 
/// If a trigger activates and said trigger has a tracker matching the event's channel, the
/// state of that channel shall become Residual instead. When a channel is in residual state,
/// the next KEY_UP event matching that channel gets dropped. After dropping a KEY_UP event,
/// the state of the corresponding channel returns to undefined. Furthermore, a KEY_DOWN event
/// arriving to a channel in Residual state cancels the Residual state and sets it back to
/// Withheld.
#[derive(Debug, Clone, Copy)]
enum ChannelState {
    Withheld { withheld_event: Event },
    Residual,
}
0707010000005A000081A4000000000000000000000001647DB230000018C3000000000000000000000000000000000000002200000000evsieve-1.4.0~0/src/subprocess.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::process::{Command, Stdio, Child};
use std::io;
use std::sync::Mutex;
use crate::signal::{SigMask, SignalFd};
use crate::error::{Context, SystemError};
use crate::io::epoll::{Epoll, Message};

lazy_static! {
    /// Keeps track of all subprocess we've spawned so we can terminate them when evsieve exits.
    static ref MANAGER: Mutex<SubprocessManager> = Mutex::new(SubprocessManager::new());
}

/// Tries to terminate all subprocesses.
pub fn terminate_all() {
    match MANAGER.lock() {
        Ok(mut lock) => lock.terminate_all(),
        Err(_) => eprintln!("Failed to terminate subprocesses: internal lock poisoned."),
    }
}

/// Will spawn a process. The process will be SIGTERM'd when `subprocess::terminate_all` is called
/// (if it is still running by then).
pub fn try_spawn(program: String, args: Vec<String>) -> Result<(), SystemError> {
    // Compute a printable version of the command, so we have something to show the
    // user in case an error happens.
    let printable_cmd: String = vec![program.clone()].into_iter().chain(args.iter().map(
        |arg| if arg.contains(' ') {
            format!("\"{}\"", arg)
        } else {
            arg.clone()
        }
    )).collect::<Vec<String>>().join(" ");

    // I checked the stdlib source code: the process' signal mask will be reset for the child process.
    let child_res: Result<Child, io::Error> =
        Command::new(program)
        .args(args)
        .stdin(Stdio::null())
        .spawn();
    let child = match child_res {
        Ok(proc) => proc,
        Err(error) => {
            return Err(SystemError::from(error).with_context(
                format!("While trying to run {}:", printable_cmd)
            ));
        }
    };

    let process = Subprocess {
        child, printable_cmd
    };

    MANAGER.lock().expect("Internal lock poisoned.").add_process(process);
    Ok(())
}

struct SubprocessManager {
    processes: Vec<Subprocess>,
    cleanup_thread_is_running: bool,
}

impl SubprocessManager {
    fn new() -> SubprocessManager {
        SubprocessManager {
            processes: Vec::new(),
            cleanup_thread_is_running: false,
        }
    }

    /// Tries to free the resources of all finished processes.
    fn cleanup(&mut self) {
        self.processes = self.processes.drain(..).filter_map(Subprocess::try_cleanup).collect();
    }

    fn add_process(&mut self, process: Subprocess) {
        self.processes.push(process);

        if ! self.cleanup_thread_is_running {
            start_cleanup_thread();
            self.cleanup_thread_is_running = true;
        }
    }

    /// Tries to terminate all subprocesses.
    fn terminate_all(&mut self) {
        for process in self.processes.drain(..) {
            process.terminate();
        }
    }
}

struct Subprocess {
    printable_cmd: String,
    child: Child,
}

impl Subprocess {
    /// Tries to wait on self. If the process has exited, then returns None, signalling that the
    /// process has been cleaned up. Otherwise, returns Some(self), signalling that it must be
    /// cleaned up at some later time.
    #[must_use]
    pub fn try_cleanup(mut self) -> Option<Subprocess> {
        match self.child.try_wait() {
            Err(error) => {
                eprintln!("Error while waiting on {}: {}", self.printable_cmd, error);
                None
            },
            Ok(status_opt) => match status_opt {
                // If None, then the subprocess has not exited yet.
                None => Some(self),
                // If Some, then the process has exited.
                Some(status) => if status.success() {
                    None
                } else {
                    match status.code() {
                        Some(code) => eprintln!("Failed to run {}: return code {}.", self.printable_cmd, code),
                        None => eprintln!("Failed to run {}: interrupted by signal.", self.printable_cmd),
                    };
                    None
                }
            }
        }
    }

    pub fn terminate(self) {
        // Make sure the process hasn't already exited before we try to clean it up.
        if let Some(mut process) = self.try_cleanup() {
            // Send a SIGTERM signal.
            unsafe { libc::kill(process.child.id() as i32, libc::SIGTERM) };
            // Wait for it so the operating system cleans up resources.
            std::thread::spawn(move || process.child.wait());
        }
    }
}

fn start_cleanup_thread() {
    std::thread::spawn(move || {
        // This thread waits until a SIGCHLD signal is received and then tries to clean up lingering
        // subprocesses.
        let mut sigmask = SigMask::new();
        sigmask.add(libc::SIGCHLD);
        let signal_fd = SignalFd::new(&sigmask)
            .expect("Subprocess cleanup thread failed to create a signal fd.");

        // Using an Epoll to wait for signal information to become available is necessary because
        // reading the SignalFd directly results in a WouldBlock I/O error.
        let mut epoll: Epoll<SignalFd> = Epoll::new()
            .expect("Subprocess cleanup thread failed to create an epoll.");
        epoll.add_file(signal_fd)
            .expect("Subprocess cleanup thread failed to register a signal fd with an epoll.");

        loop {
            for message in epoll.poll(crate::io::epoll::INDEFINITE_TIMEOUT).expect("Failed to poll an epoll.") {
                match message {
                    Message::Ready(index) => {
                        // If we get here, then the SignalFd should have a SIGCHLD signal ready.
                        // Any other situation is a bug.
                        let siginfo = epoll[index].read_raw()
                            .expect("Subprocess cleanup thread failed to read its signal fd.");
                        let signal_no = siginfo.ssi_signo as i32;
                        assert!(signal_no == libc::SIGCHLD);
                        MANAGER.lock().expect("Internal lock poisoned.").cleanup();
                    },
                    Message::Broken(_index) | Message::Hup(_index) => {
                        panic!("Signal fd in subprocess cleanup thread broken.");
                    }
                }
            }
        }
    });
}0707010000005B000081A4000000000000000000000001647DB23000001071000000000000000000000000000000000000001C00000000evsieve-1.4.0~0/src/time.rs// SPDX-License-Identifier: GPL-2.0-or-later

//! A reimplementation of the standard library's time module that is guaranteed to correspond
//! to clock_gettime (Monotonic Clock). Although the standard library corresponds to that
//! as well, the documentation says that it may change over time, therefore we need our own
//! time module.

use std::mem::MaybeUninit;
use std::convert::TryInto;
use crate::bindings::libevdev;

const NANOSECONDS_PER_SECOND: i64 = 1_000_000_000;
const NANOSECONDS_PER_MICROSECOND: i64 = 1_000;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Instant {
    // Second and nanoseconds since some arbitrary start point.
    sec: i64,
    // Expects invariant: 0 <= nsec < NANOSECONDS_PER_SECOND
    nsec: i64,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Duration {
    // Expects invariant: 0 <= sec
    sec: u64,
    // Expects invariant: 0 <= nsec < NANOSECONDS_PER_SECOND
    nsec: u64,
}

impl Instant {
    pub fn now() -> Instant {
        unsafe {
            let mut timespec: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
            let result = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut timespec as *mut _ as *mut libc::timespec);
            if result < 0 {
                panic!("Failed to determine the current time using [libc] clock_gettime(). Error code: {}",
                    std::io::Error::last_os_error().raw_os_error().map(|x| x.to_string()).unwrap_or_else(|| "(unknown)".to_owned())
                );
            }

            timespec.assume_init().into()
        }
    }

    // The checked part refers to making sure that self is after other.
    // Panic in case of integer overflow.
    pub fn checked_duration_since(self, other: Instant) -> Option<Duration> {
        let mut nsec = self.nsec - other.nsec;
        let mut sec = self.sec - other.sec;
        if nsec < 0 {
            nsec += NANOSECONDS_PER_SECOND;
            sec -= 1;
        }

        Some(Duration {
            sec: sec.try_into().ok()?,
            nsec: nsec.try_into().ok()?
        })
    }
}

impl From<libc::timespec> for Instant {
    #[allow(clippy::unnecessary_cast)]
    fn from(timespec: libc::timespec) -> Self {
        // Some platforms use different datatypes for tv_sec and tv_nsec than we internally
        // use, so we need these casts.
        Self {
            sec: timespec.tv_sec as i64,
            nsec: timespec.tv_nsec as i64,
        }
    }
}

impl From<libevdev::timeval> for Instant {
    #[allow(clippy::unnecessary_cast)]
    fn from(timeval: libevdev::timeval) -> Self {
        Self {
            sec: timeval.tv_sec as i64,
            nsec: NANOSECONDS_PER_MICROSECOND * timeval.tv_usec as i64,
        }
    }
}

impl Duration {
    pub fn from_secs(sec: u64) -> Duration {
        Duration::from_nanos(sec * 1_000_000_000)
    }

    pub fn from_millis(msec: u64) -> Duration {
        Duration::from_nanos(msec * 1_000_000)
    }

    pub fn from_micros(microsec: u64) -> Duration {
        Duration::from_nanos(microsec * 1_000)
    }

    pub fn from_nanos(nsec: u64) -> Duration {
        Duration {
            sec: nsec / NANOSECONDS_PER_SECOND as u64,
            nsec: nsec % NANOSECONDS_PER_SECOND as u64
        }
    }

    pub fn as_millis(self) -> u64 {
        self.sec * 1_000 + self.nsec / 1_000_000
    }
}

impl std::ops::Add<Duration> for Instant {
    type Output = Instant;
    fn add(self, rhs: Duration) -> Self::Output {
        let mut sec = self.sec + rhs.sec as i64;
        let mut nsec = self.nsec + rhs.nsec as i64;
        if nsec > NANOSECONDS_PER_SECOND {
            nsec -= NANOSECONDS_PER_SECOND;
            sec += 1;
        }
        
        Instant { sec, nsec }
    }
}

#[test]
fn unittest() {
    let now = Instant::now();

    assert_eq!(
        now + Duration::from_secs(3),
        now + Duration::from_millis(500) + Duration::from_micros(1_700_000) + Duration::from_nanos(800_000_000)
    );
    assert_eq!(
        (now + Duration::from_secs(3)).checked_duration_since(now),
        Some(Duration::from_millis(3000))
    );
    assert_eq!(
        now.checked_duration_since(now + Duration::from_secs(3)),
        None
    );
}0707010000005C000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001A00000000evsieve-1.4.0~0/src/utils0707010000005D000081A4000000000000000000000001647DB23000000729000000000000000000000000000000000000001D00000000evsieve-1.4.0~0/src/utils.rs// SPDX-License-Identifier: GPL-2.0-or-later

use std::sync::Mutex;
use std::ffi::CStr;
use libc::c_char;

pub mod shelllex;

pub fn split_once<'a>(value: &'a str, deliminator: &str) -> (&'a str, Option<&'a str>) {
    let mut splitter = value.splitn(2, deliminator);
    (splitter.next().unwrap(), splitter.next())
}

/// Tries to turn a raw pointer into a String.
/// Returns None if the pointer is a null-pointer, or the string is not valid UTF8.
///
/// # Safety
/// The pointer must be either the null-pointer, or have a valid trailing 0-byte.
pub unsafe fn parse_cstr(raw_ptr: *const c_char) -> Option<String> {
    if raw_ptr.is_null() {
        return None;
    }
    let raw_cstr: &CStr = CStr::from_ptr(raw_ptr);
    raw_cstr.to_str().ok().map(str::to_string)
}

lazy_static!{
    static ref PRINTED_WARNINGS: Mutex<Vec<String>> = Mutex::new(Vec::new());
}

/// Prints a warning to the user using stderr, but only prints each unique message once.
pub fn warn_once(message: impl Into<String>) {
    let message: String = message.into();
    if let Ok(mut printed_warnings) = PRINTED_WARNINGS.lock() {
        if ! printed_warnings.contains(&message) {
            eprintln!("{}", message);
            printed_warnings.push(message);
        }
    } else {
        eprintln!("Warning: internal lock poisoned.");
        eprintln!("{}", message);
    }
}

/// Like str::parse, but a bit more stringent, in that it also rejects floats containing e+ or NaN.
/// Also interprets "" as 1.0 and "-" as -1.0.
pub fn parse_number(string: &str) -> Option<f64> {
    const ALLOWED_CHARS: &str = "0123456789-.";
    match string {
        "-" => Some(-1.0),
        "" => Some(1.0),
        _ => if string.chars().all(|c| ALLOWED_CHARS.contains(c)) {
            string.parse().ok()
        } else {
            None
        }
    }
}
0707010000005E000081A4000000000000000000000001647DB23000002487000000000000000000000000000000000000002600000000evsieve-1.4.0~0/src/utils/shelllex.rs// SPDX-License-Identifier: GPL-2.0-or-later

//! This module is responsible for splitting strings into tokens in a way similar
//! to how a shell would do it. For example, the following string:
//! 
//!     --hook key:a exec-shell="echo Hello, world!"
//! 
//! Should be split into three tokens: "--hook", "key:a" and "exec-shell=Hello, world!".
//! Yes, removing the quotes around <<Hello, world!>> is intentional.

use crate::error::ArgumentError;

#[derive(Clone, Copy)]
enum State {
    /// Nothing special about the next character.    
    Normal,
    /// The next character is part of a comment.
    Comment,
    /// The next character is part of a string.
    Quoted(QuoteMark),
}

#[derive(Clone, Copy)]
enum MaybeEscapedState {
    /// We are currently processing the character after a \ character.
    /// After processing it, return to the state contained.
    Escaped(State),
    /// We are not processing the character escaped by a \ character.
    NotEscaped(State),
}

/// Used to distinguish single-quoted strings from double-quoted strings.
#[derive(Clone, Copy)]
enum QuoteMark {
    Single,
    Double,
}

impl QuoteMark {
    fn as_char(&self) -> char {
        match self {
            QuoteMark::Single => '\'',
            QuoteMark::Double => '\"',
        }
    }

    fn try_from(character: char) -> Option<QuoteMark> {
        match character {
            '\'' => Some(QuoteMark::Single),
            '\"' => Some(QuoteMark::Double),
            _ => None,
        }
    }
}

// TODO: FEATURE(config) Should we also interpret the `# ... ` sequence, since we use it in
// quite a lot of our own examples?
// TODO: FEATURE(config) Should we treat \r\n the same as we treat \n?

/// Tries to split a string into tokens in a way similar to how a shell does it.
pub fn lex(input: &str) -> Result<Vec<String>, ArgumentError> {
    let mut state = MaybeEscapedState::NotEscaped(State::Normal);
    let mut next_token: Option<String> = None;
    let mut tokens: Vec<String> = Vec::new();

    // Read characters from the input and append them to next_token.
    //
    // Unless the character is whitespace, in which case the current token shall be
    // finalized, meaning that future characters must be written to a new token.
    //
    // Unless some control character gets encountered, in which case the state is modified
    // according to that control character.
    //
    // Unless...
    //
    // You get the gist. I can't summarize the next 80 lines in a comment.
    for character in input.chars() {
        match state {
            // Handle generic characters that are not under any special mode of processing.
            MaybeEscapedState::NotEscaped(State::Normal) => {
                match character {
                    '#' => {
                        finalize_token(&mut tokens, &mut next_token);
                        state = MaybeEscapedState::NotEscaped(State::Comment);
                    },
                    '\\' => {
                        state = MaybeEscapedState::Escaped(State::Normal);
                    },
                    '\'' | '\"' => {
                        if next_token.is_none() {
                            next_token = Some(String::new());
                        }
                        state = MaybeEscapedState::NotEscaped(State::Quoted(
                            QuoteMark::try_from(character).unwrap()
                        ));
                    },
                    _ if character.is_whitespace() => {
                        finalize_token(&mut tokens, &mut next_token);
                    },
                    _ => {
                        push_to_token(&mut next_token, character);
                    },
                }
            },

            // Handle characters in comments.
            MaybeEscapedState::NotEscaped(State::Comment) => {
                if character == '\n' {
                    state = MaybeEscapedState::NotEscaped(State::Normal);
                } else {
                    // Ignore character.
                }
            },

            // Handle characters inside a string.
            MaybeEscapedState::NotEscaped(quote_state @ State::Quoted(used_quote_mark)) => {
                match character {
                    mark if mark == used_quote_mark.as_char() => {
                        state = MaybeEscapedState::NotEscaped(State::Normal);
                    },
                    '\\' => {
                        state = MaybeEscapedState::Escaped(quote_state);
                    },
                    _ => {
                        push_to_token(&mut next_token, character);
                    }
                } 
            },

            // Handle escaped characters after a backslash (\) character.
            MaybeEscapedState::Escaped(last_state) => {
                // A backslash before a newline causes that newline to be ignored.
                if character == '\n' {
                    state = MaybeEscapedState::NotEscaped(last_state);
                    continue;
                }

                // TODO: Expand the following list.
                let mapped_char = match character {
                    'n' => '\n',
                    't' => '\t',
                    '\\' => '\\',
                    '\'' => '\'',
                    '\"' => '\"',
                    '#' => '#',
                    '*' => '*',
                    '?' => '?',
                    ' ' => ' ',
                    _ => return Err(ArgumentError::new(format!(
                        "Unknown escape sequence encountered: \\{}", character
                    ))),
                };

                push_to_token(&mut next_token, mapped_char);
                state = MaybeEscapedState::NotEscaped(last_state);
            }
        }
    }

    // All characters have been read. Make sure we are in a valid state now.
    match state {
        MaybeEscapedState::Escaped(_) => {
            return Err(ArgumentError::new("Encountered an escape character (\\) at end of stream."));
        },
        MaybeEscapedState::NotEscaped(State::Quoted(quote_char)) => {
            return Err(ArgumentError::new(format!(
                "Reached end-of-stream before finding the end of a string: {}{}",
                quote_char.as_char(),
                next_token.unwrap_or_default(),
            )));
        }
        MaybeEscapedState::NotEscaped(State::Normal | State::Comment) => {
            finalize_token(&mut tokens, &mut next_token);
        }
    }

    Ok(tokens)
}

/// Adds a character to the token that is currently being accumulated. Creates a new
/// token if no token is currently being accumulated.
fn push_to_token(token: &mut Option<String>, character: char) {
    match token {
        Some(string) => string.push(character),
        None => {
            *token = Some(character.into());
        },
    }
}

/// If a token was getting accumulated, finalize it in the sense that no new character
/// can be added to it anymore. Sets the currently accumulating token to empty, ensuring
/// that all future characters will be added to a new token.
fn finalize_token(tokens: &mut Vec<String>, token: &mut Option<String>) {
    if let Some(item) = token.take() {
        tokens.push(item);
    }
}

// TODO: Write some more unittests.
#[test]
fn unittest() {
    assert_eq!(
        lex("--hook exec-shell=\"echo Hello, world!\"").unwrap(),
        vec!["--hook".to_owned(), "exec-shell=echo Hello, world!".to_owned()]
    );
    assert_eq!(
        lex("foo \"bar\" 'baz' \"q\"u'u'\"x\"").unwrap(),
        vec!["foo".to_owned(), "bar".to_owned(), "baz".to_owned(), "quux".to_owned()],
    );
    assert_eq!(
        lex("foo bar # baz \nquux").unwrap(),
        vec!["foo".to_owned(), "bar".to_owned(), "quux".to_owned()],
    );
    assert_eq!(
        lex("foo bar # baz \nquux").unwrap(),
        vec!["foo".to_owned(), "bar".to_owned(), "quux".to_owned()],
    );
    assert_eq!(
        lex("a b\\ c").unwrap(),
        vec!["a".to_owned(), "b c".to_owned()],
    );
    assert_eq!(
        lex("foo \"bar 'baz' \\\"quux\\\"\"").unwrap(),
        vec!["foo".to_owned(), "bar 'baz' \"quux\"".to_owned()],
    );
    assert_eq!(
        lex("foo # \\\nbar").unwrap(),
        vec!["foo".to_owned(), "bar".to_owned()],
    );
    assert_eq!(
        lex("foo\\\nbar").unwrap(),
        vec!["foobar".to_owned()],
    );
    assert_eq!(
        lex("").unwrap(),
        Vec::<String>::new(),
    );
    assert_eq!(
        lex(" ").unwrap(),
        Vec::<String>::new(),
    );
    assert_eq!(
        lex("\t\n").unwrap(),
        Vec::<String>::new(),
    );
    assert_eq!(
        lex("\"\"").unwrap(),
        vec!["".to_owned()],
    );
    assert_eq!(
        lex("--map \"\" key:a").unwrap(),
        vec!["--map".to_owned(), "".to_owned(), "key:a".to_owned()],
    );
    assert_eq!(
        lex("   foo    ").unwrap(),
        vec!["foo".to_owned()],
    );
    assert_eq!(
        lex("   foo  \\  ").unwrap(),
        vec!["foo".to_owned(), " ".to_owned()],
    );
    assert_eq!(
        lex("foo \" # \" '#' \\# bar # baz").unwrap(),
        vec!["foo".to_owned(), " # ".to_owned(), "#".to_owned(), "#".to_owned(), "bar".to_owned()],
    );
    

    lex("foo \"bar").unwrap_err();
    lex("foo \\").unwrap_err();
    lex("foo \"'").unwrap_err();
}
0707010000005F000041ED000000000000000000000002647DB23000000000000000000000000000000000000000000000001900000000evsieve-1.4.0~0/unittest07070100000060000081ED000000000000000000000001647DB23000000A83000000000000000000000000000000000000002400000000evsieve-1.4.0~0/unittest/control.py#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later

import evdev
import evdev.ecodes as e
import os
import subprocess as sp
import time

EVSIEVE_PATH = "target/debug/evsieve"

TEST_DEVICE_PATH_IN = "/dev/input/by-id/unittest-control-in"
TEST_DEVICE_PATH_OUT = "/dev/input/by-id/unittest-control-out"
CONTROL_FIFO_PATH = "/tmp/evsieve-unittest-control-fifo"

if os.path.exists(CONTROL_FIFO_PATH):
    os.remove(CONTROL_FIFO_PATH)

capabilities = { e.EV_KEY: [e.KEY_A] }
input_device = evdev.UInput(capabilities)
if os.path.exists(TEST_DEVICE_PATH_IN) or os.path.islink(TEST_DEVICE_PATH_IN):
    raise Exception(f"Cannot carry out the unittest: required path is already occupied.")
os.symlink(input_device.device, TEST_DEVICE_PATH_IN)

sp.run([
    "systemd-run", "-Gu", "evsieve-unittest.service", "--service-type=notify", "--",
    EVSIEVE_PATH,
    "--input", TEST_DEVICE_PATH_IN, "grab=force",
    "--toggle", "", "key:b", "key:c",
    "--control-fifo", CONTROL_FIFO_PATH,
    "--output", f"create-link={TEST_DEVICE_PATH_OUT}",
])

if not os.path.exists(TEST_DEVICE_PATH_OUT):
    raise Exception("Unit test failed: systemd-run has returned but the output device does not yet exist.")

output_device = evdev.InputDevice(TEST_DEVICE_PATH_OUT)
output_device.grab()
assert(os.path.exists(CONTROL_FIFO_PATH))

def write_events(events):
    for event in events:
        input_device.write(*event)
        input_device.syn()

def expect_events(expected_events):
    events_read = []
    for event in output_device.read():
        event = (event.type, event.code, event.value)
        if event[0] != e.EV_SYN:
            events_read.append(event)
    if events_read != expected_events:
        raise Exception("Unit test failed: invalid program output.")

try:
    for i in range(5):
        with open(CONTROL_FIFO_PATH, "w") as file:
            file.write("toggle\n")
        time.sleep(0.01)

        write_events([(e.EV_KEY, e.KEY_A, 1), (e.EV_KEY, e.KEY_A, 0)])
        time.sleep(0.01)
        expect_events([(e.EV_KEY, e.KEY_C, 1), (e.EV_KEY, e.KEY_C, 0)])

        with open(CONTROL_FIFO_PATH, "w") as file:
            file.write("toggle\n")
        time.sleep(0.01)

        write_events([(e.EV_KEY, e.KEY_A, 1), (e.EV_KEY, e.KEY_A, 0)])
        time.sleep(0.01)
        expect_events([(e.EV_KEY, e.KEY_B, 1), (e.EV_KEY, e.KEY_B, 0)])

    success = True
except Exception as error:
    success = False
    print("Unittest failed: ", error)


output_device.close()
sp.run(["systemctl", "stop", "evsieve-unittest.service"])
input_device.close()
os.unlink(TEST_DEVICE_PATH_IN)

assert(not os.path.exists(CONTROL_FIFO_PATH))

if success:
    print("Unittest successful.")

07070100000061000081ED000000000000000000000001647DB23000002601000000000000000000000000000000000000002300000000evsieve-1.4.0~0/unittest/reopen.py#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later

import evdev
import evdev.ecodes as e
from typing import *
import os
import subprocess as sp
import time

EVSIEVE_PROGRAM = ["target/debug/evsieve"]
SLEEP_TIME = 0.2

sp.run(["systemctl", "reset-failed"])
def run_with_args(args):
    sp.run(["systemd-run", "--service-type=notify", "--collect", "--unit=evsieve-unittest.service"] + EVSIEVE_PROGRAM + args)

def terminate_subprocess():
    sp.run(["systemctl", "stop", "evsieve-unittest.service"])

# Part 1. Test whether evsieve reopens devices.

output_path = "/dev/input/by-id/evsieve-unittest-reopen-out"
symlink_chain = [
    "/dev/input/by-id/evsieve-unittest-reopen",
    "/tmp/evsieve-unittest/foo/link1",
    "/tmp/evsieve-unittest/bar/link2",
]

capabilities = {e.EV_KEY: [e.KEY_A]}
input_device = evdev.UInput(capabilities)

def create_links():
    global input_device

    current_path = input_device.device
    for link in symlink_chain:
        if os.path.exists(link) or os.path.islink(link):
            os.unlink(link)
        os.makedirs(os.path.dirname(link), exist_ok=True)
        os.symlink(current_path, link)
        current_path = link

create_links()

run_with_args([
    "--input", symlink_chain[-1], "grab=force", "persist=reopen",
    "--output", f"create-link={output_path}"
])

output_device = evdev.InputDevice(output_path)
output_device.grab()

def test_send_events():
    global input_device
    global output_device

    expected_events = [(e.EV_KEY, e.KEY_A, 1), (e.EV_SYN, 0, 0), (e.EV_KEY, e.KEY_A, 0), (e.EV_SYN, 0, 0)]

    for event in expected_events:
        input_device.write(event[0], event[1], event[2])
    time.sleep(0.01)

    received_events = 0
    for expected_event, real_event in zip(expected_events, output_device.read()):
        event = (real_event.type, real_event.code, real_event.value)
        assert(expected_event == event)
        received_events += 1
    assert(received_events == len(expected_events))

test_send_events()

# Destroy the device and then recreate it.
input_device.close()
for link in symlink_chain:
    os.unlink(link)
time.sleep(SLEEP_TIME)

input_device = evdev.UInput(capabilities)
create_links()
time.sleep(SLEEP_TIME)

# Test whether evsieve has picked up the new device.
test_send_events()

# This time only destroy the last link in the chain.
input_device.close()
os.unlink(symlink_chain[0])
time.sleep(SLEEP_TIME)

input_device = evdev.UInput(capabilities)
os.symlink(input_device.device, symlink_chain[0])
time.sleep(SLEEP_TIME)

test_send_events()

print("Unittest part 1 successful.")

terminate_subprocess()
input_device.close()
output_device.close()
time.sleep(0.2)

# Part 2: testing whether evsieve exits when all devices are closed with persist=none.
output_path = "/dev/input/by-id/evsieve-unittest-reopen-out"
assert(not os.path.islink(output_path))

symlink_chain = [
    "/tmp/evsieve-unittest/link1",
    "/tmp/evsieve-unittest/link2",
]

capabilities_A = {e.EV_KEY: [e.KEY_A]}
capabilities_B = {e.EV_KEY: [e.KEY_B]}
capabilities_C = {e.EV_KEY: [e.KEY_C]}
capabilities_AB = {e.EV_KEY: [e.KEY_A, e.KEY_B]}

input_device_1 = None
input_device_2 = None
input_device_1_path = "/tmp/evsieve-unittest/link1"
input_device_2_path = "/tmp/evsieve-unittest/link2"

def link_to_device(device, link):
    if os.path.exists(link) or os.path.islink(link):
        os.unlink(link)
    os.makedirs(os.path.dirname(link), exist_ok=True)
    os.symlink(device.device, link)

def create_input_devices(capabilities):
    global input_device_1
    global input_device_2
    # The links need to be deleted before output devices are created, otherwise a race condition happens:
    # Upon creating input_device_1, evsieve may create a virtual output device that just happens to be
    # at the location that input_device_2_path points to before input_device_2 is created.
    for link in [input_device_1_path, input_device_2_path]:
        if os.path.exists(link) or os.path.islink(link):
            os.unlink(link)
    input_device_1 = evdev.UInput(capabilities)
    input_device_2 = evdev.UInput(capabilities)
    link_to_device(input_device_1, input_device_1_path)
    link_to_device(input_device_2, input_device_2_path)

create_input_devices(capabilities_A)


run_with_args([
    "--input", input_device_1_path, "persist=none", "grab=force",
    "--input", input_device_2_path, "persist=none", "grab=force",
    "--output", f"create-link={output_path}"
])
assert(os.path.exists(output_path))

input_device_1.close()
time.sleep(SLEEP_TIME)
assert(os.path.exists(output_path))

# Make sure evsieve exits when all input devices disappear.
input_device_2.close()
time.sleep(SLEEP_TIME)
assert(not os.path.exists(output_path) and not os.path.islink(output_path))

print("Unittest part 2 successful.")

# Part 3: test whether evsieve handles devices with changing capabilities properly.

create_input_devices(capabilities_AB)
run_with_args([
    "--input", input_device_1_path, "persist=reopen", "grab=force",
    "--input", input_device_2_path, "persist=reopen", "grab=force",
    "--output", f"create-link={output_path}"
])
time.sleep(SLEEP_TIME)
output_device = evdev.InputDevice(output_path)
output_device.grab()

def test_events(input_device, output_device, events):
    for event in events:
        input_device.write(event[0], event[1], event[2])
    time.sleep(0.05)

    received_events = 0
    for expected_event, real_event in zip(events, output_device.read()):
        event = (real_event.type, real_event.code, real_event.value)
        assert(expected_event == event)
        received_events += 1
    assert(received_events == len(events))

test_events(input_device_2, output_device,
    [(e.EV_KEY, e.KEY_A, 1), (e.EV_SYN, 0, 0), (e.EV_KEY, e.KEY_A, 0), (e.EV_SYN, 0, 0)]
)

# Upon closing an input device, all pressed keys should be released.
input_device_1.write(e.EV_KEY, e.KEY_A, 1)
input_device_1.write(e.EV_SYN, 0, 0)
input_device_1.close()

time.sleep(0.05)
expected_output = [(e.EV_KEY, e.KEY_A, 1), (e.EV_SYN, 0, 0), (e.EV_KEY, e.KEY_A, 0), (e.EV_SYN, 0, 0)]
real_output = [(event.type, event.code, event.value) for event in output_device.read()]
assert(expected_output == real_output)

# Upon recreating a device with the same or lesser capabilities, the output device should not be recreated.
os.unlink(input_device_1_path)
input_device_1 = evdev.UInput(capabilities_A)
link_to_device(input_device_1, input_device_1_path)

time.sleep(SLEEP_TIME)
test_events(input_device_1, output_device,
    [(e.EV_KEY, e.KEY_A, 1), (e.EV_SYN, 0, 0), (e.EV_KEY, e.KEY_A, 0), (e.EV_SYN, 0, 0)]
)
test_events(input_device_2, output_device,
    [(e.EV_KEY, e.KEY_B, 1), (e.EV_SYN, 0, 0), (e.EV_KEY, e.KEY_B, 0), (e.EV_SYN, 0, 0)]
)

# Upon recreating a device with more capabilities, the output device should be recreated.
input_device_1.close()
os.unlink(input_device_1_path)
input_device_1 = evdev.UInput(capabilities_C)
link_to_device(input_device_1, input_device_1_path)
time.sleep(SLEEP_TIME)

output_device.close()
output_device = evdev.InputDevice(output_path)
output_device.grab()

test_events(input_device_1, output_device,
    [(e.EV_KEY, e.KEY_C, 1), (e.EV_SYN, 0, 0), (e.EV_KEY, e.KEY_C, 0), (e.EV_SYN, 0, 0)]
)
test_events(input_device_2, output_device,
    [(e.EV_KEY, e.KEY_B, 1), (e.EV_SYN, 0, 0), (e.EV_KEY, e.KEY_B, 0), (e.EV_SYN, 0, 0)]
)

input_device_1.close()
input_device_2.close()
output_device.close()
terminate_subprocess()
os.unlink(input_device_1_path)
os.unlink(input_device_2_path)

print("Unittest part 3 successful.")

# Part 4: make sure change in current value of the axes does not trigger device recreation.

capabilities_1 = {
    e.EV_ABS: [
        (e.ABS_X, evdev.AbsInfo(value=0, min=-255, max=255, fuzz=0, flat=0, resolution=1))
    ]
}
capabilities_2 = {
    e.EV_ABS: [
        (e.ABS_X, evdev.AbsInfo(value=100, min=-255, max=255, fuzz=0, flat=0, resolution=1))
    ]
}
capabilities_3 = {
    e.EV_ABS: [
        (e.ABS_X, evdev.AbsInfo(value=100, min=-512, max=512, fuzz=0, flat=0, resolution=1))
    ]
}

input_path = "/dev/input/by-id/evsieve-unittest-reopen-in"
output_path = "/dev/input/by-id/evsieve-unittest-reopen-out"
input_device = evdev.UInput(capabilities_1)
if os.path.islink(input_path):
    os.unlink(input_path)
os.symlink(input_device.device, input_path)

run_with_args([
    "--input", input_path, "persist=reopen", "grab=force",
    "--output", f"create-link={output_path}"
])
output_device = evdev.InputDevice(output_path)
output_device.grab()

test_events(input_device, output_device,
    [(e.EV_ABS, e.ABS_X, 42), (e.EV_SYN, 0, 0)]
)

os.unlink(input_path)
input_device.close()
time.sleep(SLEEP_TIME)

input_device = evdev.UInput(capabilities_2)
os.symlink(input_device.device, input_path)
time.sleep(SLEEP_TIME)

# Because capabilities_1 and capabilities_2 do only differ in the current value of the axes, this
# should not trigger recreation of the output devices.
test_events(input_device, output_device,
    [(e.EV_ABS, e.ABS_X, 20), (e.EV_SYN, 0, 0)]
)

os.unlink(input_path)
input_device.close()
time.sleep(SLEEP_TIME)

input_device = evdev.UInput(capabilities_3)
os.symlink(input_device.device, input_path)
time.sleep(SLEEP_TIME)

# Because capabilities_2 and capabilities_3 do differ in the range of the axes, this should trigger
# recreation of the output devices.
output_device.close()
output_device = evdev.InputDevice(output_path)
output_device.grab()

assert(output_device.capabilities()[e.EV_ABS] == capabilities_3[e.EV_ABS])
test_events(input_device, output_device,
    [(e.EV_ABS, e.ABS_X, 400), (e.EV_SYN, 0, 0)]
)

os.unlink(input_path)
input_device.close()
output_device.close()
terminate_subprocess()

print("Unittest part 4 successful.")
07070100000062000081ED000000000000000000000001647DB230000006CF000000000000000000000000000000000000002400000000evsieve-1.4.0~0/unittest/systemd.py#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later

import evdev
import evdev.ecodes as e
import os
import subprocess as sp
import time

EVSIEVE_PATH = "target/debug/evsieve"

TEST_DEVICE_PATH_IN = "/dev/input/by-id/unittest-systemd-in"
TEST_DEVICE_PATH_OUT = "/dev/input/by-id/unittest-systemd-out"

capabilities = { e.EV_KEY: [e.KEY_A] }
input_device = evdev.UInput(capabilities)
if os.path.exists(TEST_DEVICE_PATH_IN) or os.path.islink(TEST_DEVICE_PATH_IN):
    raise Exception(f"Cannot carry out the unittest: required path is already occupied.")
os.symlink(input_device.device, TEST_DEVICE_PATH_IN)

# This test is mainly designed to verify whether evsieve works nicely with the "notify" service type.
sp.run([
    "systemd-run", "-Gu", "evsieve-unittest.service", "--service-type=notify", "--",
    EVSIEVE_PATH,
    "--input", TEST_DEVICE_PATH_IN, "grab=force",
    "--output", f"create-link={TEST_DEVICE_PATH_OUT}",
])

if not os.path.exists(TEST_DEVICE_PATH_OUT):
    raise Exception("Unit test failed: systemd-run has returned but the output device does not yet exist.")

output_device = evdev.InputDevice(TEST_DEVICE_PATH_OUT)
output_device.grab()

input_device.write(e.EV_KEY, e.KEY_A, 1)
input_device.syn()
input_device.write(e.EV_KEY, e.KEY_A, 0)
input_device.syn()

time.sleep(0.01)

events_read = []
for event in output_device.read():
    event = (event.type, event.code, event.value)
    events_read.append(event)
if events_read != [(1, 30, 1), (0, 0, 0), (1, 30, 0), (0, 0, 0)]:
    raise Exception("Unit test failed: invalid program output.")

output_device.close()
sp.run(["systemctl", "stop", "evsieve-unittest.service"])
input_device.close()
os.unlink(TEST_DEVICE_PATH_IN)

print("Unittest successful.")

07070100000063000081ED000000000000000000000001647DB2300000D484000000000000000000000000000000000000002500000000evsieve-1.4.0~0/unittest/unittest.py#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later

import evdev
import evdev.ecodes as e
from typing import *
import os
import subprocess as sp
import time

EVSIEVE_PROGRAM = ["target/debug/evsieve"]

# Pass a delay to a input list of run_unittest to signify that it should wait a bit before
# sending the next events.
class Delay:
    period: float

    def __init__(self, period):
        self.period = period

def run_unittest(
    arguments: List[str],
    input: Dict[str, List[Union[Tuple[int, int, int], Delay]]],
    output: Dict[str, List[Tuple[int, int, int]]],
    auto_syn = True,
    expected_output = None,
):
    # Create virtual input devices.
    input_devices = dict()
    for path, events in input.items():
        non_delay_events = [
            event
            for event in events
            if not isinstance(event, Delay)
        ]
        type_capabilities = set([type for (type, _, _) in non_delay_events if type != e.EV_SYN])
        capabilities = {
            type: [
                code
                for (_type, code, _) in non_delay_events
                if _type == type
            ]
            for type in type_capabilities
        }
        input_device = evdev.UInput(capabilities)
        if os.path.exists(path) or os.path.islink(path):
            raise Exception(f"Cannot carry out the unittest: required path {path} is already occupied.")
        os.symlink(input_device.device, path)
        input_devices[input_device] = events

    # Run the actual program.
    process = sp.Popen(EVSIEVE_PROGRAM + arguments, stdout=sp.PIPE)
    # Give the process some time to create the output devices.
    time.sleep(0.2)

    try:
        # Open the output devices.
        output_devices = []
        for path, events in output.items():
            output_device = evdev.InputDevice(path)
            output_device.grab()
            output_devices.append((output_device, events))

        # Send the input events.
        output_events = [list() for dev in output_devices]
        for device, events in input_devices.items():
            for event in events:
                if isinstance(event, Delay):
                    time.sleep(event.period)
                    # Read the output events at this point in time.
                    for ((output_device, _), events) in zip(output_devices, output_events):
                        try:
                            for event in output_device.read():
                                if event.type != e.EV_SYN:
                                    events.append(event)
                        except BlockingIOError:
                            pass
                    continue

                device.write(*event)
                if auto_syn:
                    device.syn()
        
        # Final read pass.
        time.sleep(0.01)
        for ((output_device, _), events) in zip(output_devices, output_events):
            try:
                for event in output_device.read():
                    if event.type != e.EV_SYN or not auto_syn:
                        events.append(event)
            except BlockingIOError:
                pass

        # Check whether the output devices have the expected events.
        for (events, (_, expected_events)) in zip(output_events, output_devices):
            for event in events:
                expected_event = expected_events.pop(0)
                event = (event.type, event.code, event.value)
                if event != expected_event:
                    expected_event_format = f"({e.EV[expected_event[0]]}, {e.bytype[expected_event[0]][expected_event[1]]}, {expected_event[2]})"
                    event_format = f"({e.EV[event[0]]}, {e.bytype[event[0]][event[1]]}, {event[2]})"
                    raise Exception(f"Unit test failed. Expected event {expected_event_format}, encountered {event_format}")
            if len(expected_events) > 0:
                raise Exception(f"Unit test failed. Expected events {expected_events}, but the output device closed.")

    finally:
        # Clean up.
        for device, _ in output_devices:
            device.close()
        if expected_output is not None:
            time.sleep(0.2)
        process.terminate()
        for device in input_devices.keys():
            device.close()
        for path in input.keys():
            os.unlink(path)
        if expected_output is not None:
            output = process.stdout.read().decode("utf8")
            if output != expected_output:
                raise Exception(f"Unittest failed. Expected the following output:\n{expected_output}\nGot:\n{output}")


def unittest_mirror():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-mirror-in", "grab=force",
        "--output", "create-link=/dev/input/by-id/unittest-mirror-out", "repeat=passive"],
        {
            "/dev/input/by-id/unittest-mirror-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-mirror-out": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),
            ],
        },
    )

def unittest_syn():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-syn-in", "grab=force",
        "--map", "key:x", "key:y", "key:z",
        "--output", "create-link=/dev/input/by-id/unittest-syn-out", "repeat=passive"],
        {
            "/dev/input/by-id/unittest-syn-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_SYN, 0, 0),

                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_SYN, 0, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-syn-out": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_SYN, 0, 0),

                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_SYN, 0, 0),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_SYN, 0, 0),
            ],
        },
        auto_syn=False,
    )

def unittest_capslock():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-capslock-in", "grab=force",
        "--map", "key:capslock", "key:backspace",
        "--output", "create-link=/dev/input/by-id/unittest-capslock-out", "repeat=passive"],
        {
            "/dev/input/by-id/unittest-capslock-in": [
                (e.EV_KEY, e.KEY_CAPSLOCK, 1),
                (e.EV_KEY, e.KEY_CAPSLOCK, 2),
                (e.EV_KEY, e.KEY_CAPSLOCK, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-capslock-out": [
                (e.EV_KEY, e.KEY_BACKSPACE, 1),
                (e.EV_KEY, e.KEY_BACKSPACE, 2),
                (e.EV_KEY, e.KEY_BACKSPACE, 0),
            ],
        },
    )

def unittest_doublectrl():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-doublectrl-in", "grab=force",
        "--map", "key:scrolllock", "key:leftctrl", "key:rightctrl",
        "--output", "create-link=/dev/input/by-id/unittest-doublectrl-out", "repeat=passive"],
        {
            "/dev/input/by-id/unittest-doublectrl-in": [
                (e.EV_KEY, e.KEY_LEFTCTRL, 1),
                (e.EV_KEY, e.KEY_LEFTCTRL, 0),
                (e.EV_KEY, e.KEY_SCROLLLOCK, 1),
                (e.EV_KEY, e.KEY_SCROLLLOCK, 2),
                (e.EV_KEY, e.KEY_SCROLLLOCK, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-doublectrl-out": [
                (e.EV_KEY, e.KEY_LEFTCTRL, 1),
                (e.EV_KEY, e.KEY_LEFTCTRL, 0),
                (e.EV_KEY, e.KEY_LEFTCTRL, 1),
                (e.EV_KEY, e.KEY_RIGHTCTRL, 1),
                (e.EV_KEY, e.KEY_LEFTCTRL, 2),
                (e.EV_KEY, e.KEY_RIGHTCTRL, 2),
                (e.EV_KEY, e.KEY_LEFTCTRL, 0),
                (e.EV_KEY, e.KEY_RIGHTCTRL, 0),
            ],
        },
    )

def unittest_filterbyoutput():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-filterbyoutput-in", "grab=force",
        "--output", "key:a", "create-link=/dev/input/by-id/unittest-filterbyoutput-out-1",
        "--output", "create-link=/dev/input/by-id/unittest-filterbyoutput-out-2"],
        {
            "/dev/input/by-id/unittest-filterbyoutput-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_A, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-filterbyoutput-out-1": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
            ],
            "/dev/input/by-id/unittest-filterbyoutput-out-2": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
    )

def unittest_domain():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-domain-in", "domain=in", "grab=force",
        "--map", "key:a", "key:a@out-1",
        "--map", "@in", "@out-1", "@out-2",
        "--output", "@out-1", "create-link=/dev/input/by-id/unittest-domain-out-1",
        "--output", "@out-2", "create-link=/dev/input/by-id/unittest-domain-out-2"],
        {
            "/dev/input/by-id/unittest-domain-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_A, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-domain-out-1": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_A, 0),
            ],
            "/dev/input/by-id/unittest-domain-out-2": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
    )

def unittest_kbmousemap():
    args = ["--input", "/dev/input/by-id/unittest-kbmousemap-kb-in", "grab=force",
        "--input", "/dev/input/by-id/unittest-kbmousemap-mouse-in", "grab=force", "domain=mouse",
        "--map", "key:left:1~2",      "rel:x:-20@mouse",
        "--map", "key:right:1~2",     "rel:x:20@mouse",
        "--map", "key:up:1~2",        "rel:y:-20@mouse",
        "--map", "key:down:1~2",      "rel:y:20@mouse",
        "--map", "key:enter:0~1",     "btn:left@mouse",
        "--map", "key:backslash:0~1", "btn:right@mouse",
        "--output", "@mouse", "create-link=/dev/input/by-id/unittest-kbmousemap-mouse-out", "repeat=passive"]
    
    run_unittest(
        args,
        {
            "/dev/input/by-id/unittest-kbmousemap-kb-in": [
                (e.EV_KEY, e.KEY_LEFT, 1),
                (e.EV_KEY, e.KEY_LEFT, 2),
                (e.EV_KEY, e.KEY_LEFT, 2),
                (e.EV_KEY, e.KEY_LEFT, 0),
                (e.EV_KEY, e.BTN_LEFT, 1),
                (e.EV_KEY, e.BTN_LEFT, 0),
                (e.EV_KEY, e.KEY_BACKSLASH, 1),
                (e.EV_KEY, e.KEY_BACKSLASH, 2),
                (e.EV_KEY, e.KEY_BACKSLASH, 0),
            ],
            "/dev/input/by-id/unittest-kbmousemap-mouse-in": [],
        },
        {
            "/dev/input/by-id/unittest-kbmousemap-mouse-out": [
                (e.EV_REL, e.REL_X, -20),
                (e.EV_REL, e.REL_X, -20),
                (e.EV_REL, e.REL_X, -20),
                (e.EV_KEY, e.BTN_RIGHT, 1),
                (e.EV_KEY, e.BTN_RIGHT, 0),
            ],
        }
    )
    
    run_unittest(
        args,
        {
            "/dev/input/by-id/unittest-kbmousemap-kb-in": [],
            "/dev/input/by-id/unittest-kbmousemap-mouse-in": [
                (e.EV_REL, e.REL_Y, 10),
                (e.EV_KEY, e.BTN_LEFT, 1),
                (e.EV_KEY, e.BTN_LEFT, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-kbmousemap-mouse-out": [
                (e.EV_REL, e.REL_Y, 10),
                (e.EV_KEY, e.BTN_LEFT, 1),
                (e.EV_KEY, e.BTN_LEFT, 0),
            ],
        }
    )
    
def unittest_execshell():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-execshell-in", "grab=force",
        "--hook", "key:t", "exec-shell=echo trigger1",
        "--hook", "key:a", "key:b", "exec-shell=echo trigger2",
        "--hook", "key:q", "key:w", "exec-shell=echo trigger3",
        "--hook", "key:z", "key:x", "key:c", "exec-shell=echo trigger4",
        "--hook", "key:e", "key:i", "key:o", "exec-shell=echo trigger5",
        "--hook", "key:n:2", "exec-shell=echo trigger6"],
        {
            "/dev/input/by-id/unittest-execshell-in": [
                (e.EV_KEY, e.KEY_T, 1),
                (e.EV_KEY, e.KEY_T, 2),
                (e.EV_KEY, e.KEY_T, 0),
                Delay(0.01),
                (e.EV_KEY, e.KEY_T, 1),
                (e.EV_KEY, e.KEY_T, 0),
                Delay(0.01),

                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 2),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 0),
                Delay(0.01),

                (e.EV_KEY, e.KEY_Q, 1),
                (e.EV_KEY, e.KEY_W, 1),
                (e.EV_KEY, e.KEY_Q, 2),
                (e.EV_KEY, e.KEY_W, 2),
                (e.EV_KEY, e.KEY_Q, 0),
                (e.EV_KEY, e.KEY_W, 0),
                (e.EV_KEY, e.KEY_Q, 1),
                (e.EV_KEY, e.KEY_Q, 0),
                Delay(0.01),

                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_C, 1), 
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_Z, 0),
                Delay(0.01),
                
                # Should not trigger: O is not pressed.
                (e.EV_KEY, e.KEY_O, 1),
                (e.EV_KEY, e.KEY_O, 0),
                (e.EV_KEY, e.KEY_I, 1),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_I, 0),
                Delay(0.01),

                (e.EV_KEY, e.KEY_N, 1),
                (e.EV_KEY, e.KEY_N, 0),
                (e.EV_KEY, e.KEY_N, 1),
                (e.EV_KEY, e.KEY_N, 2),
                (e.EV_KEY, e.KEY_N, 2),
                (e.EV_KEY, e.KEY_N, 2),
                (e.EV_KEY, e.KEY_N, 0),
                (e.EV_KEY, e.KEY_N, 1),
                (e.EV_KEY, e.KEY_N, 2),
                (e.EV_KEY, e.KEY_N, 0),
                Delay(0.01),
            ],
        },
        {},
        expected_output = "trigger1\ntrigger1\ntrigger2\ntrigger3\ntrigger4\ntrigger6\ntrigger6\n",
    )

def unittest_sequential_hook():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-sequential-hook-in", "grab=force",
        "--hook", "key:a", "sequential", "send-key=key:x",
        "--hook", "key:b", "key:c", "sequential", "send-key=key:y",
        "--hook", "key:d", "key:e", "key:f", "sequential", "send-key=key:z",
        "--hook", "key:d", "key:e", "key:f", "send-key=key:w",
        "--output", "create-link=/dev/input/by-id/unittest-sequential-hook-out", "repeat=passive"],
        {
            "/dev/input/by-id/unittest-sequential-hook-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),

                # I don't know whether the next two groups describe the most sensible behaviour,
                # but they seem no less crazy than the alternative and they are how evsieve
                # behaves now, so that behaviour must be preserved.
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),

                Delay(0.001),

                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_F, 0),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_D, 0),

                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_F, 0),
                (e.EV_KEY, e.KEY_D, 0),
            ]
        },
        {
            "/dev/input/by-id/unittest-sequential-hook-out": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_W, 1),
                (e.EV_KEY, e.KEY_W, 0),
                (e.EV_KEY, e.KEY_F, 0),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_D, 0),

                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_W, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_W, 0),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_W, 1),
                (e.EV_KEY, e.KEY_W, 0),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_F, 0),
                (e.EV_KEY, e.KEY_D, 0),
            ]
        },
    )

def unittest_toggle():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-toggle-in", "domain=in", "grab=force",
        "--toggle", "@in", "@out-1", "@out-2", "id=output-toggle",
        "--toggle", "key:a", "key:b", "key:c", "id=a-toggle",
        "--toggle", "key:b", "key:e", "key:f",
        "--hook", "key:z", "toggle",
        "--hook", "key:x", "toggle=a-toggle",
        "--hook", "key:v", "toggle=a-toggle:1", "toggle",
        "--hook", "key:q", "toggle=a-toggle:2",
        "--output", "@out-1", "create-link=/dev/input/by-id/unittest-toggle-out-1",
        "--output", "@out-2", "create-link=/dev/input/by-id/unittest-toggle-out-2"],
    {
        "/dev/input/by-id/unittest-toggle-in": [
            (e.EV_KEY, e.KEY_A, 1),
            (e.EV_KEY, e.KEY_A, 0),
            (e.EV_KEY, e.KEY_Z, 1),
            (e.EV_KEY, e.KEY_Z, 0),
            (e.EV_KEY, e.KEY_A, 1),
            (e.EV_KEY, e.KEY_A, 0),

            (e.EV_KEY, e.KEY_A, 1),
            (e.EV_KEY, e.KEY_X, 1),
            (e.EV_KEY, e.KEY_X, 0),
            (e.EV_KEY, e.KEY_A, 0),
            (e.EV_KEY, e.KEY_A, 1),
            (e.EV_KEY, e.KEY_A, 0),
            
            (e.EV_KEY, e.KEY_V, 1),
            (e.EV_KEY, e.KEY_V, 0),
            (e.EV_KEY, e.KEY_N, 1),
            (e.EV_KEY, e.KEY_N, 0),
            (e.EV_KEY, e.KEY_A, 1),
            (e.EV_KEY, e.KEY_A, 0),

            (e.EV_KEY, e.KEY_Q, 1),
            (e.EV_KEY, e.KEY_Q, 0),
            (e.EV_KEY, e.KEY_A, 1),
            (e.EV_KEY, e.KEY_A, 0),
        ],
    },
    {
        "/dev/input/by-id/unittest-toggle-out-1": [
            (e.EV_KEY, e.KEY_E, 1),
            (e.EV_KEY, e.KEY_E, 0),
            (e.EV_KEY, e.KEY_Z, 1),
            (e.EV_KEY, e.KEY_Z, 0),

            (e.EV_KEY, e.KEY_N, 1),
            (e.EV_KEY, e.KEY_N, 0),
            (e.EV_KEY, e.KEY_E, 1),
            (e.EV_KEY, e.KEY_E, 0),

            (e.EV_KEY, e.KEY_Q, 1),
            (e.EV_KEY, e.KEY_Q, 0),
            (e.EV_KEY, e.KEY_C, 1),
            (e.EV_KEY, e.KEY_C, 0),
        ],
        "/dev/input/by-id/unittest-toggle-out-2": [
            (e.EV_KEY, e.KEY_C, 1),
            (e.EV_KEY, e.KEY_C, 0),

            (e.EV_KEY, e.KEY_C, 1),
            (e.EV_KEY, e.KEY_X, 1),
            (e.EV_KEY, e.KEY_X, 0),
            (e.EV_KEY, e.KEY_C, 0),
            (e.EV_KEY, e.KEY_F, 1),
            (e.EV_KEY, e.KEY_F, 0),

            (e.EV_KEY, e.KEY_V, 1),
            (e.EV_KEY, e.KEY_V, 0),
        ],
    })

def unittest_yield():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-yield-in", "grab=force",
        "--map", "key:a", "key:b",
        "--map", "key:b", "key:c",
        "--map", "key:d", "key:e", "yield",
        "--map", "key:e", "key:f",
        "--copy", "key:g", "key:h", "yield",
        "--copy", "key:h", "key:i",
        "--block", "key:g",
        "--output", "create-link=/dev/input/by-id/unittest-yield-out"],
        {
            "/dev/input/by-id/unittest-yield-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_G, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-yield-out": [
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_H, 0),
            ],
        },
    )

def unittest_order():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-order-in", "grab=force",
        "--copy", "key:a", "key:d", "key:f",
        "--map", "key:d", "key:d", "key:e", "yield",
        "--map", "key:a", "key:b", "key:c",
        "--copy", "key:f", "key:g",
        "--output", "create-link=/dev/input/by-id/unittest-order-out"],
        {
            "/dev/input/by-id/unittest-order-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-order-out": [
                # The order of the following events is important: it's the very thing
                # this unittest is intended to test.
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_F, 0),
                (e.EV_KEY, e.KEY_G, 0),
            ],
        },
    )

def unittest_namespace():
    run_unittest(
        ["--hook", "key:a", "exec-shell=echo foo",
        "--copy", "key:a", "key:b",
        "--input", "/dev/input/by-id/unittest-namespace-in-1", "grab=force",
        "--copy", "key:a", "key:c",
        "--input", "/dev/input/by-id/unittest-namespace-in-2", "grab=force",
        "--copy", "key:a", "key:d",
        "--hook", "key:a", "exec-shell=echo bar",
        "--output", "create-link=/dev/input/by-id/unittest-namespace-out-1",
        "--input", "/dev/input/by-id/unittest-namespace-in-3", "grab=force",
        "--copy", "key:a", "key:e",
        "--output", "create-link=/dev/input/by-id/unittest-namespace-out-2",
        "--hook", "key:a", "exec-shell=echo baz",],
        {
            "/dev/input/by-id/unittest-namespace-in-1": [
                (e.EV_KEY, e.KEY_Q, 1),
                (e.EV_KEY, e.KEY_Q, 0),
            ],
            "/dev/input/by-id/unittest-namespace-in-2": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
            ],
            "/dev/input/by-id/unittest-namespace-in-3": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-namespace-out-1": [
                (e.EV_KEY, e.KEY_Q, 1),
                (e.EV_KEY, e.KEY_Q, 0),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_D, 0),
            ],
            "/dev/input/by-id/unittest-namespace-out-2": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
        expected_output="bar\n",
    )

def unittest_consistency():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-consistency-in", "domain=foo", "grab=force",
        "--map", "key:x", "key:a@bar",
        "--toggle", "key:a", "key:b", "key:c",
        "--hook", "key:z", "toggle",
        "--output", "@foo", "create-link=/dev/input/by-id/unittest-consistency-out-1",
        "--output", "@bar", "create-link=/dev/input/by-id/unittest-consistency-out-2"],
        {
            "/dev/input/by-id/unittest-consistency-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_KEY, e.KEY_A, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-consistency-out-1": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_B, 0),
            ],
            "/dev/input/by-id/unittest-consistency-out-2": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
    )

def unittest_type():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-type-in-1", "domain=in1", "grab=force",
        "--input", "/dev/input/by-id/unittest-type-in-2", "domain=in2", "grab=force",
        "--map", "key@in1", "key:a",
        "--map", "btn", "btn:left",
        "--map", "key::2", "rel:y:3",
        "--output", "btn", "@in1", "rel", "create-link=/dev/input/by-id/unittest-type-out-1",
        "--output", "create-link=/dev/input/by-id/unittest-type-out-2"],
        {
            "/dev/input/by-id/unittest-type-in-1": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 2),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_ABS, e.ABS_X, 1),
                (e.EV_ABS, e.ABS_X, 0),
            ],
            "/dev/input/by-id/unittest-type-in-2": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.BTN_RIGHT, 1),
                (e.EV_KEY, e.BTN_RIGHT, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-type-out-1": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_REL, e.REL_Y, 3),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_ABS, e.ABS_X, 1),
                (e.EV_ABS, e.ABS_X, 0),
                (e.EV_KEY, e.BTN_LEFT, 1),
                (e.EV_KEY, e.BTN_LEFT, 0),
            ],
            "/dev/input/by-id/unittest-type-out-2": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
    )

def unittest_bynumber():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-bynumber-in", "grab=force",
        "--map", f"key:%{e.KEY_A}", f"key:%{e.KEY_B}",
        "--map", f"btn:%{e.BTN_LEFT}", f"key:%{e.KEY_C}",
        "--map", f"%{e.EV_KEY}:%{e.BTN_RIGHT}", f"abs:%{e.ABS_X}",
        "--output", f"%{e.EV_KEY}", "create-link=/dev/input/by-id/unittest-bynumber-out-1",
        "--output", f"%{e.EV_ABS}", "create-link=/dev/input/by-id/unittest-bynumber-out-2"],
        {
            "/dev/input/by-id/unittest-bynumber-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.BTN_LEFT, 1),
                (e.EV_KEY, e.BTN_LEFT, 0),
                (e.EV_KEY, e.BTN_RIGHT, 1),
                (e.EV_KEY, e.BTN_RIGHT, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-bynumber-out-1": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
            ],
            "/dev/input/by-id/unittest-bynumber-out-2": [
                (e.EV_ABS, e.ABS_X, 1),
                (e.EV_ABS, e.ABS_X, 0),
            ],
        },
    )

def unittest_merge():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-merge-in-1", "domain=in1", "grab=force",
        "--map", "key:b", "key:a",
        "--map", "key:y", "key:x",
        "--map", "key:t:1", "key:a:0",
        "--block", "key:t",
        "--merge", "key:a",
        "--output", "create-link=/dev/input/by-id/unittest-merge-out-1"],
        {
            "/dev/input/by-id/unittest-merge-in-1": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_Y, 0),

                (e.EV_KEY, e.KEY_T, 1),
                (e.EV_KEY, e.KEY_T, 0),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_ABS, e.ABS_X, 10),
                (e.EV_ABS, e.ABS_X, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-merge-out-1": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_KEY, e.KEY_Z, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_ABS, e.ABS_X, 10),
                (e.EV_ABS, e.ABS_X, 0),
            ],
        },
    )

def unittest_relative():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-relative-in", "grab=force",
        "--map", "abs:x", "rel:x:0.5d",
        "--map", "abs:y", "abs:y:-1.4x",
        "--map", "abs:rx", "abs:rx:1.5x+0.4d+1",
        "--output", "create-link=/dev/input/by-id/unittest-relative-out"],
        {
            "/dev/input/by-id/unittest-relative-in": [
                # Test evsieve's resistance to rounding errors: the first movement should be
                # rounded down, the second rounded up.
                (e.EV_ABS, e.ABS_X, 7),
                (e.EV_ABS, e.ABS_X, 10),
                (e.EV_ABS, e.ABS_X, 0),

                # Test absolute factors. Unlike delta-maps, these should always be rounded
                # by truncation.
                (e.EV_ABS, e.ABS_Y, 5),
                (e.EV_ABS, e.ABS_Y, 7),
                (e.EV_ABS, e.ABS_Y, 8),
                (e.EV_ABS, e.ABS_Y, -5),
                (e.EV_ABS, e.ABS_Y, 0),

                (e.EV_ABS, e.ABS_RX, 2),
                (e.EV_ABS, e.ABS_RX, 5),
                (e.EV_ABS, e.ABS_RX, 0),
                (e.EV_ABS, e.ABS_RX, 7),
            ],
        },
        {
            "/dev/input/by-id/unittest-relative-out": [
                (e.EV_REL, e.REL_X, 3),
                (e.EV_REL, e.REL_X, 2),
                (e.EV_REL, e.REL_X, -5),

                (e.EV_ABS, e.ABS_Y, -7),
                (e.EV_ABS, e.ABS_Y, -9),
                (e.EV_ABS, e.ABS_Y, -11),
                (e.EV_ABS, e.ABS_Y, 7),
                (e.EV_ABS, e.ABS_Y, 0),

                (e.EV_ABS, e.ABS_RX, 4),
                (e.EV_ABS, e.ABS_RX, 10),
                (e.EV_ABS, e.ABS_RX, -1),
                (e.EV_ABS, e.ABS_RX, 13),
            ],
        },
    )

def unittest_delay():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-delay-in-1", "grab=force",
        "--delay", "key:a", "key:b", "period=0.01",
        "--output", "create-link=/dev/input/by-id/unittest-delay-out-1"],
        {
            "/dev/input/by-id/unittest-delay-in-1": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 2),
                (e.EV_KEY, e.KEY_B, 0),
                Delay(0.005),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
                Delay(0.01),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 2),
                (e.EV_KEY, e.KEY_B, 0),
                Delay(0.015),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
                Delay(0.005),
            ]
        },
        {
            "/dev/input/by-id/unittest-delay-out-1": [
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 2),
                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 2),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 2),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
            ],
        },
    )

def unittest_send_key():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-send-key-in", "domain=in", "grab=force",

        # We use --block to prevent capabilities from the input device from propagating to
        # the output device in order to test whether send-key generates the appropriate
        # capabilities.
        "--map", "key:a", "@out",
        "--map", "key:b", "@out",
        "--block", "@in",

        "--hook", "key:a", "key:b", "send-key=key:x", "send-key=key:y", "send-key=key:z",
        "--output", "@out", "create-link=/dev/input/by-id/unittest-send-key-out"],
        {
            "/dev/input/by-id/unittest-send-key-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-send-key-out": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
    )

def unittest_withhold():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-withhold-in", "grab=force",
        "--hook", "key:a", "key:b", "send-key=key:x",
        "--withhold",
        "--hook", "key:c",
        "--map", "key:c", "key:c@bar",
        "--hook", "key:c@foo",
        "--hook", "key:d",
        "--hook", "key:e",
        "--withhold", "key:c", "key:d", "key:f",
        "--hook", "key:g", "key:h", "key:i",
        "--hook", "key:h", "key:j",
        "--withhold",
        "--map", "key:k", "key:k@foo",
        "--map", "key:l", "key:k@bar",
        "--hook", "key:k", "key:m",
        "--withhold",
        "--map", "key:k@bar", "key:l",
        "--output", "create-link=/dev/input/by-id/unittest-withhold-out"],
        {
            "/dev/input/by-id/unittest-withhold-in": [
                # Part 1
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_S, 1),
                (e.EV_KEY, e.KEY_S, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_T, 1),
                (e.EV_KEY, e.KEY_T, 0),

                # Part 2
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_S, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_S, 2),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_S, 0),
                (e.EV_KEY, e.KEY_B, 0),

                # Part 3
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_F, 0),

                # Read events now so we don't overflow the buffer.
                Delay(0.01),

                # Part 4
                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_H, 0),

                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_H, 0),
                (e.EV_KEY, e.KEY_I, 1),
                (e.EV_KEY, e.KEY_I, 0),
                (e.EV_KEY, e.KEY_G, 0),

                (e.EV_KEY, e.KEY_S, 1),
                (e.EV_KEY, e.KEY_S, 0),

                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_J, 1),
                (e.EV_KEY, e.KEY_G, 0),
                (e.EV_KEY, e.KEY_H, 0),
                (e.EV_KEY, e.KEY_J, 0),

                # Part 5
                (e.EV_KEY, e.KEY_K, 1),
                (e.EV_KEY, e.KEY_L, 1),
                (e.EV_KEY, e.KEY_K, 0),
                (e.EV_KEY, e.KEY_L, 0),

                (e.EV_KEY, e.KEY_S, 1),
                (e.EV_KEY, e.KEY_K, 1),
                (e.EV_KEY, e.KEY_L, 1),
                (e.EV_KEY, e.KEY_M, 1),
                (e.EV_KEY, e.KEY_K, 0),
                (e.EV_KEY, e.KEY_L, 0),
                (e.EV_KEY, e.KEY_M, 0),
                (e.EV_KEY, e.KEY_S, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-withhold-out": [
                # Part 1
                (e.EV_KEY, e.KEY_S, 1),
                (e.EV_KEY, e.KEY_S, 0),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_T, 1),
                (e.EV_KEY, e.KEY_T, 0),

                # Part 2
                (e.EV_KEY, e.KEY_S, 1),
                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_S, 2),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_KEY, e.KEY_S, 0),

                # Part 3
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_F, 0),

                # Part 4
                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_H, 0),

                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_H, 0),
                (e.EV_KEY, e.KEY_I, 1),
                (e.EV_KEY, e.KEY_I, 0),
                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_G, 0),

                (e.EV_KEY, e.KEY_S, 1),
                (e.EV_KEY, e.KEY_S, 0),

                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_G, 0),

                # Part 5
                (e.EV_KEY, e.KEY_K, 1),
                (e.EV_KEY, e.KEY_L, 1),
                (e.EV_KEY, e.KEY_K, 0),
                (e.EV_KEY, e.KEY_L, 0),

                (e.EV_KEY, e.KEY_S, 1),
                (e.EV_KEY, e.KEY_S, 0),
            ],
        },
    )


def unittest_withhold_2():
    # This unittest is currently disabled because the feature that it is meant to test
    # has been disabled. I'll leave it in the code for now because in the future it may
    # be desirable to re-enable that feature again.
    return

    run_unittest(
        ["--input", "/dev/input/by-id/unittest-withhold-2-in", "domain=foo", "grab=force",
        "--hook", "key:a", "key:b", "send-key=key:x",
        "--hook", "key:c", "send-key=key:b",
        "--withhold",
        "--hook", "key:f", "send-key=key:e",
        "--hook", "key:d", "key:e", "send-key=key:x",
        "--withhold",
        "--map", "key:z", "key:i@bar",
        "--hook", "key:g", "key:h", "send-key=key:v",
        "--hook", "key:i", "send-key=key:h",
        "--hook", "key:g", "key:h@foo", "send-key=key:y",
        "--withhold",
        "--output", "create-link=/dev/input/by-id/unittest-withhold-2-out"],
        {
            "/dev/input/by-id/unittest-withhold-2-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_KEY, e.KEY_F, 0),

                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_G, 0),
                (e.EV_KEY, e.KEY_H, 0),

                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_I, 1),
                (e.EV_KEY, e.KEY_G, 0),
                (e.EV_KEY, e.KEY_I, 0),

                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_G, 0),
                (e.EV_KEY, e.KEY_Z, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-withhold-2-out": [
                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_X, 0),

                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_X, 0),

                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_V, 1),
                (e.EV_KEY, e.KEY_V, 0),
                (e.EV_KEY, e.KEY_Y, 0),

                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Y, 0),

                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_G, 0),
                (e.EV_KEY, e.KEY_H, 0),
            ],
        },
    )

def unittest_withhold_3():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-withhold-3-in", "domain=foo", "grab=force",
        "--hook", "key:a", "abs:x:1~5", "send-key=key:x",
        "--withhold", "key",
        "--output", "create-link=/dev/input/by-id/unittest-withhold-3-out"],
        {
            "/dev/input/by-id/unittest-withhold-3-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_ABS, e.ABS_X, 3),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_ABS, e.ABS_X, 7),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),

            ],
        },
        {
            "/dev/input/by-id/unittest-withhold-3-out": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_ABS, e.ABS_X, 3),
                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_X, 0),

                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_X, 0),
                (e.EV_ABS, e.ABS_X, 7),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
            ],
        },
    )

def unittest_withhold_period():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-withhold-period-in", "grab=force",
        "--hook", "key:a", "key:b", "send-key=key:x", "period=0.004",
        "--hook", "key:a", "key:c", "key:d", "send-key=key:w", "period=0.005",
        "--withhold",
        "--output", "create-link=/dev/input/by-id/unittest-withhold-period-out"],
        {
            "/dev/input/by-id/unittest-withhold-period-in": [
                (e.EV_KEY, e.KEY_A, 1),
                Delay(0.002),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_A, 1),
                Delay(0.005),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_A, 1),
                Delay(0.002),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_A, 1),
                Delay(0.005),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_D, 1),
                Delay(0.002),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_A, 1),
                Delay(0.001),
                (e.EV_KEY, e.KEY_C, 1),
                Delay(0.003),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_C, 1),
                Delay(0.003),
                (e.EV_KEY, e.KEY_D, 1),
                Delay(0.003),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_A, 1),
                Delay(0.001),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_KEY, e.KEY_A, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-withhold-period-out": [
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_X, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_KEY, e.KEY_X, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_Z, 1),
                (e.EV_KEY, e.KEY_W, 1),
                (e.EV_KEY, e.KEY_Z, 0),
                (e.EV_KEY, e.KEY_W, 0),

                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_Y, 1),
                (e.EV_KEY, e.KEY_Y, 0),
                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_W, 1),
                (e.EV_KEY, e.KEY_W, 0),
            ],
        },
    )

def unittest_withhold_sequential():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-withhold-sequential-in", "grab=force",
        "--hook", "key:a", "key:b", "key:c", "send-key=key:d", "sequential",
        "--withhold",
        "--output", "create-link=/dev/input/by-id/unittest-withhold-sequential-out"],
        {
            "/dev/input/by-id/unittest-withhold-sequential-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_C, 1),

                (e.EV_KEY, e.KEY_F, 0),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_C, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_G, 0),

                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_H, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-withhold-sequential-out": [
                (e.EV_KEY, e.KEY_F, 1),
                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_F, 0),
                (e.EV_KEY, e.KEY_D, 0),

                (e.EV_KEY, e.KEY_G, 1),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_G, 0),

                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_H, 1),
                (e.EV_KEY, e.KEY_B, 0),
                (e.EV_KEY, e.KEY_H, 0),

                (e.EV_KEY, e.KEY_E, 1),
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_A, 0),
                (e.EV_KEY, e.KEY_E, 0),
                (e.EV_KEY, e.KEY_B, 0),
            ],
        },
    )

def unittest_withhold_sequential_2():
    run_unittest(
        ["--input", "/dev/input/by-id/unittest-withhold-sequential-2-in", "grab=force",
        "--hook", "key:a", "key:b", "key:c", "key:d", "key:e", "key:f", "send-key=key:x", "sequential",
        "--withhold",
        "--output", "create-link=/dev/input/by-id/unittest-withhold-sequential-2-out"],
        {
            "/dev/input/by-id/unittest-withhold-sequential-2-in": [
                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_E, 1),

                (e.EV_KEY, e.KEY_B, 0),
                
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_KEY, e.KEY_E, 0),
            ],
        },
        {
            "/dev/input/by-id/unittest-withhold-sequential-2-out": [
                (e.EV_KEY, e.KEY_B, 1),
                (e.EV_KEY, e.KEY_C, 1),
                (e.EV_KEY, e.KEY_D, 1),
                (e.EV_KEY, e.KEY_E, 1),

                (e.EV_KEY, e.KEY_B, 0),

                (e.EV_KEY, e.KEY_A, 1),
                (e.EV_KEY, e.KEY_A, 0),

                (e.EV_KEY, e.KEY_C, 0),
                (e.EV_KEY, e.KEY_D, 0),
                (e.EV_KEY, e.KEY_E, 0),
            ],
        },
    )

unittest_mirror()
unittest_syn()
unittest_capslock()
unittest_doublectrl()
unittest_filterbyoutput()
unittest_domain()
unittest_kbmousemap()
unittest_execshell()
unittest_sequential_hook()
unittest_toggle()
unittest_yield()
unittest_order()
unittest_namespace()
unittest_consistency()
unittest_type()
unittest_bynumber()
unittest_merge()
unittest_relative()
unittest_delay()
unittest_send_key()
unittest_withhold()
unittest_withhold_2()
unittest_withhold_3()
unittest_withhold_period()
unittest_withhold_sequential()
unittest_withhold_sequential_2()
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1308 blocks
openSUSE Build Service is sponsored by