README.md

# InputEvent

[![Hex version](https://img.shields.io/hexpm/v/input_event.svg "Hex version")](https://hex.pm/packages/input_event)
[![API docs](https://img.shields.io/hexpm/v/input_event.svg?label=hexdocs "API docs")](https://hexdocs.pm/input_event)
[![CircleCI](https://dl.circleci.com/status-badge/img/gh/nerves-web-kiosk/input_event/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/nerves-web-kiosk/input_event/tree/main)

Elixir interface to Linux input event devices. Using `input_event`, you can

* Find out what keyboards, joysticks, mice, touchscreens, etc. are connected
* Get information on what kinds of events they can produce
* Get decoded events sent to you

This library is intended for use with Nerves. If you're running a desktop Linux
distribution, this can still work, but you'll likely want to use a higher level
API for receiving events.

## Usage

InputEvent can be used to monitor `/dev/input/event*` devices and report decoded
data to the parent process. The event reports follow the [Linux
documentation](https://elixir.bootlin.com/linux/latest/source/Documentation/input/event-codes.rst)
fairly closely, but it's easiest to understand by example.

Start by looking for the device you want to monitor:

```elixir
iex> InputEvent.enumerate
[
  {"/dev/input/event0",
   %InputEvent.Info{
     bus: 0,
     input_event_version: "1.0.1",
     name: "FT5406 memory based driver",
     product: 0,
     report_info: [
       ev_abs: [
         abs_x: %{flat: 0, fuzz: 0, max: 800, min: 0, resolution: 0, value: 0},
         abs_y: %{flat: 0, fuzz: 0, max: 480, min: 0, resolution: 0, value: 0},
         abs_mt_slot: %{
           flat: 0,
           fuzz: 0,
           max: 9,
           min: 0,
           resolution: 0,
           value: 0
         },
         abs_mt_position_x: %{
           flat: 0,
           fuzz: 0,
           max: 800,
           min: 0,
           resolution: 0,
           value: 0
         },
         abs_mt_position_y: %{
           flat: 0,
           fuzz: 0,
           max: 480,
           min: 0,
           resolution: 0,
           value: 0
         },
         abs_mt_tracking_id: %{
           flat: 0,
           fuzz: 0,
           max: 65535,
           min: 0,
           resolution: 0,
           value: 0
         }
       ],
       ev_key: [:btn_touch]
     ],
     vendor: 0,
     version: 0
   }}
]
```

There's a touchscreen at `/dev/input/event0`, so let's open it:

```elixir
iex> InputEvent.start_link("/dev/input/event0")
{:ok, #PID<0.197.0>}
```

Touch the screen to test

```elixir
iex> flush
{:input_event, "/dev/input/event0",
 [
   {:ev_abs, :abs_mt_tracking_id, 1},
   {:ev_abs, :abs_mt_position_x, 350},
   {:ev_abs, :abs_mt_position_y, 119},
   {:ev_key, :btn_touch, 1},
   {:ev_abs, :abs_x, 350},
   {:ev_abs, :abs_y, 119}
 ]}
{:input_event, "/dev/input/event0",
 [
   {:ev_abs, :abs_mt_position_x, 352},
   {:ev_abs, :abs_mt_position_y, 122},
   {:ev_abs, :abs_x, 352},
   {:ev_abs, :abs_y, 122}
 ]}
{:input_event, "/dev/input/event0",
 [{:ev_abs, :abs_mt_tracking_id, -1}, {:ev_key, :btn_touch, 0}]}
{:input_event, "/dev/input/event0",
 [
   {:ev_abs, :abs_mt_tracking_id, 2},
   {:ev_abs, :abs_mt_position_x, 361},
   {:ev_abs, :abs_mt_position_y, 361},
   {:ev_abs, :abs_mt_slot, 1},
   {:ev_abs, :abs_mt_tracking_id, 3},
   {:ev_abs, :abs_mt_position_x, 425},
   {:ev_abs, :abs_mt_position_y, 139},
   {:ev_key, :btn_touch, 1},
   {:ev_abs, :abs_x, 361},
   {:ev_abs, :abs_y, 361}
 ]}
{:input_event, "/dev/input/event0", [{:ev_abs, :abs_mt_position_y, 142}]}
{:input_event, "/dev/input/event0",
 [
   {:ev_abs, :abs_mt_slot, 0},
   {:ev_abs, :abs_mt_position_y, 363},
   {:ev_abs, :abs_mt_slot, 1},
   {:ev_abs, :abs_mt_position_x, 427},
   {:ev_abs, :abs_mt_position_y, 147},
   {:ev_abs, :abs_y, 363}
 ]}
{:input_event, "/dev/input/event0",
 [{:ev_abs, :abs_mt_position_x, 428}, {:ev_abs, :abs_mt_position_y, 149}]}
{:input_event, "/dev/input/event0",
 [
   {:ev_abs, :abs_mt_slot, 0},
   {:ev_abs, :abs_mt_tracking_id, -1},
   {:ev_abs, :abs_mt_slot, 1},
   {:ev_abs, :abs_mt_tracking_id, -1},
   {:ev_key, :btn_touch, 0}
 ]}
:ok
```

## Examples

Enumerating attached devices can be really helpful in figuring out what kind of
messages that you'll be sent. Here are other examples:

### Keyboard

```elixir
  {"/dev/input/event3",
   %InputEvent.Info{
     bus: 3,
     input_event_version: "1.0.1",
     name: "Logitech K520",
     product: 8209,
     report_info: [
       ev_rep: %{delay: 250, period: 33},
       ev_led: [:led_numl, :led_capsl, :led_scrolll, :led_compose, :led_kana],
       ev_msc: [:msc_scan],
       ev_abs: [
         abs_volume: %{
           flat: 0,
           fuzz: 0,
           max: 652,
           min: 1,
           resolution: 0,
           value: 0
         }
       ],
       ev_rel: [:rel_hwheel],
       ev_key: [:key_esc, :key_1, :key_2, :key_3, :key_4, :key_5, :key_6,
        :key_7, :key_8, :key_9, :key_0, :key_minus, :key_equal, :key_backspace,
        :key_tab, :key_q, :key_w, :key_e, :key_r, :key_t, :key_y, :key_u,
        :key_i, :key_o, :key_p, :key_leftbrace, :key_rightbrace, ...]
     ],
     vendor: 1133,
     version: 273
   }}
```

Events from a keyboard look like:

```elixir
{:input_event, "/dev/input/event3", [{:ev_key, :key_f, 1}]}
{:input_event, "/dev/input/event3", [{:ev_key, :key_f, 0}]}
```

In the tuple, `{:ev_key, :key_f, 1}`, the third element is `0` for key up, `1`
for key down, and `2` for key repeat.

### Mouse

```elixir
  {"/dev/input/event2",
   %InputEvent.Info{
     bus: 3,
     input_event_version: "1.0.1",
     name: "Logitech M310",
     product: 4132,
     report_info: [
       ev_msc: [:msc_scan],
       ev_rel: [:rel_x, :rel_y, :rel_hwheel, :rel_wheel],
       ev_key: [:btn_left, :btn_right, :btn_middle, :btn_side, :btn_extra,
        :btn_forward, :btn_back, :btn_task, 280, 281, 282, 283, 284, 285, 286,
        287]
     ],
     vendor: 1133,
     version: 273
   }}
```

Events from a mouse look like:

```elixir
{:input_event, "/dev/input/event2", [{:ev_rel, :rel_x, 12}]}
{:input_event, "/dev/input/event2",
 [{:ev_rel, :rel_x, 14}, {:ev_rel, :rel_y, -1}]}
```

Notice that if there's no movement in an axis that you won't get an update for that axis.

### Joystick

```elixir
  {"/dev/input/event15",
   %InputEvent.Info{
     bus: 3,
     input_event_version: "1.0.1",
     name: "Microsoft X-Box 360 pad",
     product: 654,
     report_info: [
       ev_ff: 'PQXYZ`',
       ev_abs: [
         abs_x: %{
           flat: 128,
           fuzz: 16,
           max: 32767,
           min: -32768,
           resolution: 0,
           value: 0
         },
         abs_y: %{
           flat: 128,
           fuzz: 16,
           max: 32767,
           min: -32768,
           resolution: 0,
           value: 0
         },
         abs_z: %{flat: 0, fuzz: 0, max: 255, min: 0, resolution: 0, value: 0},
         abs_rx: %{
           flat: 128,
           fuzz: 16,
           max: 32767,
           min: -32768,
           resolution: 0,
           value: 0
         },
         abs_ry: %{
           flat: 128,
           fuzz: 16,
           max: 32767,
           min: -32768,
           resolution: 0,
           value: 0
         },
         abs_rz: %{flat: 0, fuzz: 0, max: 255, min: 0, resolution: 0, value: 0},
         abs_hat0x: %{
           flat: 0,
           fuzz: 0,
           max: 1,
           min: -1,
           resolution: 0,
           value: 0
         },
         abs_hat0y: %{
           flat: 0,
           fuzz: 0,
           max: 1,
           min: -1,
           resolution: 0,
           value: 0
         }
       ],
       ev_key: [:btn_a, :btn_b, :btn_x, :btn_y, :btn_tl, :btn_tr, :btn_select,
        :btn_start, :btn_mode, :btn_thumbl, :btn_thumbr]
     ],
     vendor: 1118,
     version: 272
   }}
```

### Power button

```elixir
  {"/dev/input/event0",
   %InputEvent.Info{
     bus: 25,
     input_event_version: "1.0.1",
     name: "Power Button",
     product: 1,
     report_info: [ev_key: [:key_power]],
     vendor: 0,
     version: 0
   }}
```

The power button input device works just like a one-button keyboard.

## Grab device

Devices can be grabbed to prevent output into other applications.

```elixir
iex> InputEvent.start_link(path: "/dev/input/event0", grab: true)
{:ok, #PID<0.197.0>}
```

### Permissions

To be able to read from `/dev/input/event*` you need to add user to the `input`
group:

```sh
sudo usermod -a -G input <username>
```

And restart user session.