# [TECH] [2022. VII] DIY Macro-keyboard

Using a short Python script, an extra keyboard can easily be configured to serve as custom macro-keyboard.

The script relies on the `evdev`package that provides and interface for passing events generated in the kernel directly to userspace. Simulating key presses (and controlling the mouse) can be done with `xdotool`(which allows access to all keys - binded or not), or with Python's `pyautogui`package.

## Setting up and running the macro-keyboard script

(1) Detect the input event associated to the keyboard that will be used as a macro-keyboard by examining `/proc/bus/input/devices`. The keyboard will topically be listed multiple times but only one entry will have have contain `Handlers=sysrq kbd leds eventXY` - event of that entry is the keyboard input event.

(2) Modify the following script to contain the correct input event and desired macros.

```python
#!/usr/bin/env python
import os
import pyautogui
from evdev import InputDevice, categorize, ecodes
dev = InputDevice('/dev/input/eventXY')
dev.grab()

for event in dev.read_loop():
    if event.type == ecodes.EV_KEY:
        key = categorize(event)
        if key.keystate == key.key_down:
            if key.keycode == 'KEY_A':
                ...
            if key.keycode == 'KEY_B':
                ...
```

(3) Run the script as root.

### Example macro-keyboard script

```python
#!/usr/bin/env python
import os
import pyautogui
from evdev import InputDevice, categorize, ecodes
dev = InputDevice('/dev/input/event21')
dev.grab()

for event in dev.read_loop():
    if event.type == ecodes.EV_KEY:
        key = categorize(event)
        if key.keystate == key.key_down:
            if key.keycode == 'KEY_Q':
                os.system('echo "Macro-Keyboard has been used!"')  # call a command
            if key.keycode == 'KEY_W':
                os.system('xdotool key dead_greek+Y')              # use xdotool to simulate key presses
            if key.keycode == 'KEY_E':
                pyautogui.hotkey('win', 'a')                       # use pyautogui to simulate key presses
            if key.keycode == 'KEY_R':                             # use pyautogui to control the mouse
                pyautogui.move(100, 0)
```

## Appendix: `xdotool` commands
```
| Command              | Description                                              | Example                                |
| -------------------- | -------------------------------------------------------- | -------------------------------------- |
| `key`                | Simulate a key stroke.                                   | `xdotool key a`                        |
|                      | Simulate a key stroke with a modifier.                   | `xdotool key crtl+a`                   |
|                      | Simulate repeated key strokes.                           | `xdotool key --repeat 10 --delay 50 a` |
|                      | Simulate a key stroke sequence.                          | `xdotool key a s d`                    |
| `keydown`            | Simulate a key press.                                    | `xdotool keydown a`                    |
| `keyup`              | Simulate a key relese.                                   | `xdotool keyup a`                      |
| `click`              | Simulate mouse click [1 - left, 2 - middle, 3]           | `xdotool click 3`                      |
| `mousemove`          | Move the cursor to given point.                          | `xdotool mousemove 100 100`            |
| `mousemove_relative` | Move cursor to a point relative to the current position. | `xdotool mousemove_relative 100 100`   |
```

## Appendix: `pyautogui` commands
```
| Command     | Description                                                          | Example                                          |
| ----------- | -------------------------------------------------------------------- | ------------------------------------------------ |
| `press`     | Simulate a key stroke.                                               | `pyautogui.press('a')`                           |
| `hotkey`    | Simulate pressing multiple keys and releasing them in reverse order. | `pyautogui.hotkey('a', 's', 'd')`                |
| `keyDown`   | Simulate a key press.                                                | `pyautogui.keyDown('a')`                         |
| `keyUp`     | Simulate a key release.                                              | `pyautogui.keyUp('a')`                           |
| `write`     | Type the characters in the string.                                   | `pyautogui.write('Greetins!')`                   |
| `typewrite` | Type the characters in the string wherever at the cursor location.   | `pyautogui.typewrite('Greetins!')`               |
| `click`     | Move the cursor and preform clicks.                                  | `pyautogui.click()`                              |
|             |                                                                      | `pyautogui.click(button='right')`                |
|             |                                                                      | ``pyautogui.click(x=100, y=100, button='right')` |
| `moveTo`    | Move the cursor.                                                     | `pyautogui.moveTo(100,100)`                      |
| `move`      | Move the cursor to a point relative to the current position.         | `pyautogui.move(100,0)`                          |
```