# How to Send Keyboard Events and Key Combinations with Zendriver

> Learn to send keyboard events and key combinations with Zendriver using Python. Utilize the KeyEvents class and Element send_keys method for efficient automation.

- Repository: [CDP Driver/zendriver](https://github.com/cdpdriver/zendriver)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Zendriver converts high-level Python calls into Chrome DevTools Protocol (CDP) keyboard commands using the `KeyEvents` class and `Element.send_keys()` method.**

Zendriver is a Python driver for Chrome DevTools Protocol (CDP) that provides granular control over browser automation. Sending keyboard events and key combinations with Zendriver requires understanding three core abstractions defined in [`zendriver/core/keys.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/keys.py): the modifier bitmask enum, the special keys enum, and the payload generator class. These components translate Python strings and tuples into CDP-compatible `dispatch_key_event` payloads.

## Understanding the Keyboard Architecture

Zendriver's keyboard system is built around three interconnected components that handle the translation from Python objects to CDP commands.

### KeyModifiers Enum

The `KeyModifiers` class is an `IntEnum` located at lines 8-21 of [`zendriver/core/keys.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/keys.py). It represents modifier keys as bitmasks:

- `KeyModifiers.Ctrl`
- `KeyModifiers.Alt` 
- `KeyModifiers.Shift`
- `KeyModifiers.Meta`

You combine modifiers using the bitwise OR operator (`|`):

```python
from zendriver import KeyModifiers

ctrl_shift = KeyModifiers.Ctrl | KeyModifiers.Shift

```

### SpecialKeys Enum

`SpecialKeys` (lines 23-41) defines non-printable keys such as **Enter**, **Escape**, and **Arrow** keys. Each enum entry stores the DOM key name and its corresponding virtual-key code. Available options include `SpecialKeys.ENTER`, `SpecialKeys.ESCAPE`, `SpecialKeys.ARROW_DOWN`, and `SpecialKeys.TAB`.

### KeyEvents Class

The `KeyEvents` class (lines 45-95) serves as the central translator. It converts key descriptions into lists of CDP payload dictionaries expected by `cdp.input_.dispatch_key_event`. The class handles:

- Key code lookup for printable and non-printable keys
- Shifted character normalization (automatically adding `Shift` flag for uppercase letters)
- Modifier bitmask validation

## Sending Basic Keyboard Input

The `Element.send_keys()` method in [`zendriver/core/element.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/element.py) (lines 38-60) is the primary entry point for dispatching keyboard events.

### Sending Plain Text

For standard text input, pass a string directly to `send_keys()`:

```python
await element.send_keys("Hello, world!")

```

Internally, this calls `KeyEvents.from_text("Hello, world!", KeyPressEvent.CHAR)`, which generates a series of `char` events for each character in the string.

### Sending Special Keys

To press non-printable keys like Enter or Escape, pass a `SpecialKeys` enum value:

```python
from zendriver import SpecialKeys

await element.send_keys(SpecialKeys.ENTER)
await element.send_keys(SpecialKeys.ESCAPE)

```

The method wraps the enum in a `KeyEvents` instance and emits it as a **DOWN_AND_UP** sequence, triggering both key press and release events automatically.

## Handling Modifier Combinations

To send keyboard shortcuts like Ctrl+A or Ctrl+V, use a tuple containing the key and a `KeyModifiers` bitmask.

### Single Modifier Shortcuts

Pass a tuple of `(key, modifiers)` to `send_keys()`:

```python
from zendriver import KeyModifiers

await element.send_keys(("a", KeyModifiers.Ctrl))  # Select all

await element.send_keys(("v", KeyModifiers.Ctrl))  # Paste

```

The `_normalise_key` method (lines 33-40 in [`keys.py`](https://github.com/cdpdriver/zendriver/blob/main/keys.py)) automatically handles uppercase letters by adding the `Shift` flag and returning the non-shifted base key. If you attempt to use an unsupported combination, the library raises a descriptive `ValueError`.

### Complex Modifier Masks

Combine multiple modifiers for shortcuts like Ctrl+Shift+T:

```python
modifiers = KeyModifiers.Ctrl | KeyModifiers.Shift
await element.send_keys(("t", modifiers))

```

## Complex Mixed Input Sequences

For scenarios requiring text, special keys, and modifiers in a single operation, use `KeyEvents.from_mixed_input()`. This helper accepts a list containing:

- **Strings** – split into individual character events
- **SpecialKeys** – emitted as DOWN_AND_UP sequences  
- **Tuples** – `(key, modifiers)` pairs for shortcuts

### Mixed Sequence Example

```python
from zendriver import SpecialKeys, KeyModifiers, KeyEvents, KeyPressEvent

payload = KeyEvents.from_mixed_input(
    [
        "Hello ",                              # Normal text

        SpecialKeys.ENTER,                     # New line

        ("a", KeyModifiers.Ctrl),             # Ctrl+A (select all)

        ("c", KeyModifiers.Ctrl),             # Ctrl+C (copy)

        SpecialKeys.ARROW_DOWN,                # Navigate down

        ("v", KeyModifiers.Ctrl),             # Ctrl+V (paste)

        " – pasted 😊",                        # Unicode text including emoji

    ],
    ascii_keypress=KeyPressEvent.DOWN_AND_UP   # Use down-up for ASCII chars

)

await element.send_keys(payload)

```

The `from_mixed_input()` method iterates through the list and delegates to the appropriate handler based on type. Strings route through `from_text()`, `SpecialKeys` trigger `to_cdp_events()`, and tuples instantiate new `KeyEvents(key, modifiers)` objects. The resulting payload list matches the shape required by `cdp.input_.dispatch_key_event`, which `Element.send_keys()` dispatches in a loop.

## Summary

- **Zendriver** converts Python keyboard instructions into CDP `dispatch_key_event` commands via the `KeyEvents` class in [`zendriver/core/keys.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/keys.py).
- **Use `Element.send_keys()`** for all keyboard input; it accepts strings, `SpecialKeys` enums, or pre-built payload lists.
- **Combine modifiers** with the bitwise OR operator (`|`) on `KeyModifiers` values like `KeyModifiers.Ctrl | KeyModifiers.Shift`.
- **Send complex sequences** using `KeyEvents.from_mixed_input()`, which handles mixed lists of text, special keys, and modifier tuples.
- **Reference implementations** reside in [`zendriver/core/keys.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/keys.py) (payload generation) and [`zendriver/core/element.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/element.py) (dispatch logic).

## Frequently Asked Questions

### How do I send Ctrl+A to select all text in Zendriver?

Pass a tuple containing the lowercase key and the Ctrl modifier to `Element.send_keys()`: `await element.send_keys(("a", KeyModifiers.Ctrl))`. The `KeyEvents` class automatically normalizes the key and sets the correct modifier bitmask in the CDP payload.

### What is the difference between KeyPressEvent.CHAR and DOWN_AND_UP?

`KeyPressEvent.CHAR` generates single `char` events suitable for typing text into input fields, while `KeyPressEvent.DOWN_AND_UP` generates separate `keyDown` and `keyUp` events required for special keys and shortcuts. The `from_text()` method defaults to `CHAR`, whereas `SpecialKeys` automatically use `DOWN_AND_UP`.

### Can I send emoji and special characters with send_keys?

Yes. Zendriver handles Unicode characters including emoji through `KeyEvents.from_text()` or `from_mixed_input()`. The library does not limit character encoding, allowing payloads like `"pasted 😊"` to pass through to the CDP layer correctly.

### Where does the actual CDP dispatch happen?

The actual network dispatch occurs in `Element.send_keys()` at [`zendriver/core/element.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/element.py) lines 55-60, which loops through the payload list and calls `await self._tab.send(cdp.input_.dispatch_key_event(**cluster))` for each event cluster generated by the `KeyEvents` class.