or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdkeyboard.mdmouse.md

keyboard.mddocs/

0

# Keyboard Control and Monitoring

1

2

Comprehensive keyboard input simulation and monitoring functionality for programmatic key presses, string typing, modifier key combinations, hotkey detection, and real-time keyboard event listening across multiple operating systems.

3

4

## Capabilities

5

6

### Keyboard Controller

7

8

The Controller class provides programmatic control over keyboard input, enabling automation of key presses, text typing, and complex key combinations.

9

10

```python { .api }

11

class Controller:

12

"""A controller for sending virtual keyboard events to the system."""

13

14

class InvalidKeyException(Exception):

15

"""Raised when an invalid key parameter is passed."""

16

pass

17

18

class InvalidCharacterException(Exception):

19

"""Raised when an untypable character is encountered in type()."""

20

pass

21

22

def __init__(self):

23

"""Initialize the keyboard controller."""

24

...

25

26

def press(self, key: Key | KeyCode | str):

27

"""

28

Press a key.

29

30

Args:

31

key: The key to press. Can be:

32

- Single character string (e.g., 'a', 'A', '1')

33

- Key enum member (e.g., Key.enter, Key.ctrl)

34

- KeyCode instance

35

36

Raises:

37

InvalidKeyException: If the key is invalid

38

ValueError: If key is string with length != 1

39

"""

40

...

41

42

def release(self, key: Key | KeyCode | str):

43

"""

44

Release a key.

45

46

Args:

47

key: The key to release (same format as press())

48

49

Raises:

50

InvalidKeyException: If the key is invalid

51

ValueError: If key is string with length != 1

52

"""

53

...

54

55

def tap(self, key: Key | KeyCode | str):

56

"""

57

Press and immediately release a key.

58

59

Args:

60

key: The key to tap (same format as press())

61

62

Raises:

63

InvalidKeyException: If the key is invalid

64

ValueError: If key is string with length != 1

65

"""

66

...

67

68

def touch(self, key: Key | KeyCode | str, is_press: bool):

69

"""

70

Press or release a key based on boolean flag.

71

72

Args:

73

key: The key to touch

74

is_press (bool): True to press, False to release

75

76

Raises:

77

InvalidKeyException: If the key is invalid

78

"""

79

...

80

81

def pressed(self, *keys) -> ContextManager:

82

"""

83

Context manager that keeps keys pressed for the duration of the block.

84

85

Args:

86

*keys: Keys to keep pressed

87

88

Returns:

89

ContextManager: Context manager for the key press duration

90

91

Example:

92

with keyboard.pressed(Key.ctrl):

93

keyboard.tap('c') # Ctrl+C

94

"""

95

...

96

97

def type(self, string: str):

98

"""

99

Type a string by sending key press and release events.

100

101

Args:

102

string (str): The string to type

103

104

Raises:

105

InvalidCharacterException: If an untypable character is encountered

106

"""

107

...

108

109

@property

110

def modifiers(self) -> ContextManager:

111

"""Context manager for accessing current modifier key state."""

112

...

113

114

@property

115

def alt_pressed(self) -> bool:

116

"""

117

Whether any alt key is pressed.

118

119

Note: This reflects only the internal state of this controller.

120

See modifiers property for more information.

121

"""

122

...

123

124

@property

125

def alt_gr_pressed(self) -> bool:

126

"""

127

Whether altgr is pressed.

128

129

Note: This reflects only the internal state of this controller.

130

See modifiers property for more information.

131

"""

132

...

133

134

@property

135

def ctrl_pressed(self) -> bool:

136

"""

137

Whether any ctrl key is pressed.

138

139

Note: This reflects only the internal state of this controller.

140

See modifiers property for more information.

141

"""

142

...

143

144

@property

145

def shift_pressed(self) -> bool:

146

"""

147

Whether any shift key is pressed, or caps lock is toggled.

148

149

Note: This reflects only the internal state of this controller.

150

See modifiers property for more information.

151

"""

152

...

153

```

154

155

#### Usage Examples

156

157

```python

158

from pynput.keyboard import Key, Controller

159

160

# Create controller

161

keyboard = Controller()

162

163

# Type individual characters

164

keyboard.press('h')

165

keyboard.release('h')

166

167

# Use tap for press+release

168

keyboard.tap('i')

169

170

# Type special keys

171

keyboard.press(Key.enter)

172

keyboard.release(Key.enter)

173

174

# Type strings

175

keyboard.type('Hello, World!')

176

177

# Key combinations using context manager

178

with keyboard.pressed(Key.ctrl):

179

keyboard.tap('c') # Ctrl+C

180

181

# Multiple modifiers

182

with keyboard.pressed(Key.ctrl, Key.shift):

183

keyboard.tap('a') # Ctrl+Shift+A

184

185

# Manual modifier handling

186

keyboard.press(Key.alt)

187

keyboard.tap(Key.tab) # Alt+Tab

188

keyboard.release(Key.alt)

189

190

# Uppercase typing

191

keyboard.tap('A') # Types uppercase A

192

# OR

193

with keyboard.pressed(Key.shift):

194

keyboard.tap('a') # Also types uppercase A

195

```

196

197

### Keyboard Event Listener

198

199

The Listener class monitors keyboard events in real-time, providing callbacks for key press and release events.

200

201

```python { .api }

202

class Listener:

203

"""A listener for keyboard events."""

204

205

def __init__(

206

self,

207

on_press: callable = None,

208

on_release: callable = None,

209

suppress: bool = False,

210

**kwargs

211

):

212

"""

213

Initialize the keyboard event listener.

214

215

Args:

216

on_press (callable): Callback for key press events (key, injected)

217

on_release (callable): Callback for key release events (key, injected)

218

suppress (bool): Whether to suppress events system-wide

219

**kwargs: Platform-specific options

220

"""

221

...

222

223

def start(self):

224

"""Start the listener thread."""

225

...

226

227

def stop(self):

228

"""Stop the listener. Cannot be restarted once stopped."""

229

...

230

231

def wait(self):

232

"""Wait for the listener to become ready."""

233

...

234

235

def join(self, timeout: float = None):

236

"""

237

Wait for the listener thread to complete.

238

239

Args:

240

timeout (float): Maximum time to wait in seconds

241

"""

242

...

243

244

def canonical(self, key) -> Key | KeyCode:

245

"""Convert a key to its canonical representation."""

246

...

247

248

@property

249

def running(self) -> bool:

250

"""Whether the listener is currently running."""

251

...

252

253

@property

254

def suppress(self) -> bool:

255

"""Whether events are being suppressed system-wide."""

256

...

257

```

258

259

#### Usage Examples

260

261

```python

262

from pynput import keyboard

263

264

def on_press(key, injected=False):

265

"""Handle key press events."""

266

try:

267

# Alphanumeric key

268

print(f'Alphanumeric key {key.char} pressed')

269

except AttributeError:

270

# Special key

271

print(f'Special key {key} pressed')

272

273

# Stop on escape

274

if key == keyboard.Key.esc:

275

return False

276

277

def on_release(key, injected=False):

278

"""Handle key release events."""

279

print(f'Key {key} released')

280

281

# Context manager usage (recommended)

282

with keyboard.Listener(

283

on_press=on_press,

284

on_release=on_release

285

) as listener:

286

listener.join()

287

288

# Manual control

289

listener = keyboard.Listener(

290

on_press=on_press,

291

on_release=on_release

292

)

293

listener.start()

294

listener.join()

295

296

# Non-blocking usage

297

listener = keyboard.Listener(on_press=on_press)

298

listener.start()

299

# ... do other work

300

listener.stop()

301

```

302

303

### Keyboard Events Iterator

304

305

The Events class provides synchronous iteration over keyboard events.

306

307

```python { .api }

308

class Events:

309

"""A keyboard event listener supporting synchronous iteration over events."""

310

311

def __init__(self):

312

"""Initialize the events iterator."""

313

...

314

315

def __enter__(self):

316

"""Start the event listener."""

317

...

318

319

def __exit__(self, *args):

320

"""Stop the event listener."""

321

...

322

323

def __iter__(self):

324

"""Return iterator interface."""

325

...

326

327

def __next__(self):

328

"""Get the next event."""

329

...

330

331

def get(self, timeout: float = None):

332

"""

333

Get the next event with optional timeout.

334

335

Args:

336

timeout (float): Maximum time to wait for an event

337

338

Returns:

339

Event or None: The next event, or None if timeout or stopped

340

"""

341

...

342

343

class Press:

344

"""A key press event."""

345

def __init__(self, key: Key | KeyCode, injected: bool):

346

self.key = key

347

self.injected = injected

348

349

class Release:

350

"""A key release event."""

351

def __init__(self, key: Key | KeyCode, injected: bool):

352

self.key = key

353

self.injected = injected

354

```

355

356

#### Usage Examples

357

358

```python

359

from pynput.keyboard import Events

360

361

# Process events synchronously

362

with Events() as events:

363

for event in events:

364

if isinstance(event, Events.Press):

365

print(f'Key {event.key} pressed')

366

if str(event.key) == "'q'": # Exit on 'q'

367

break

368

elif isinstance(event, Events.Release):

369

print(f'Key {event.key} released')

370

```

371

372

### HotKey Support

373

374

The HotKey class enables detection of key combinations, while GlobalHotKeys provides a convenient listener for multiple hotkeys.

375

376

```python { .api }

377

class HotKey:

378

"""A combination of keys acting as a hotkey."""

379

380

def __init__(self, keys: set, on_activate: callable):

381

"""

382

Initialize a hotkey.

383

384

Args:

385

keys (set): Set of keys that must be pressed simultaneously

386

on_activate (callable): Function to call when hotkey is activated

387

"""

388

...

389

390

@staticmethod

391

def parse(keys: str) -> list:

392

"""

393

Parse a key combination string.

394

395

Args:

396

keys (str): Key combination string (e.g., '<ctrl>+<alt>+h', '<cmd>+c')

397

398

Returns:

399

list: List of key objects

400

401

Raises:

402

ValueError: If the key string is invalid

403

"""

404

...

405

406

def press(self, key: Key | KeyCode):

407

"""

408

Update hotkey state for a pressed key.

409

410

Args:

411

key: The key being pressed

412

"""

413

...

414

415

def release(self, key: Key | KeyCode):

416

"""

417

Update hotkey state for a released key.

418

419

Args:

420

key: The key being released

421

"""

422

...

423

424

class GlobalHotKeys(Listener):

425

"""A keyboard listener supporting multiple global hotkeys."""

426

427

def __init__(self, hotkeys: dict, *args, **kwargs):

428

"""

429

Initialize global hotkeys listener.

430

431

Args:

432

hotkeys (dict): Mapping from hotkey strings to callback functions

433

*args, **kwargs: Arguments passed to base Listener class

434

435

Raises:

436

ValueError: If any hotkey description is invalid

437

"""

438

...

439

```

440

441

#### Usage Examples

442

443

```python

444

from pynput.keyboard import HotKey, GlobalHotKeys, Key

445

446

# Single hotkey

447

def on_hotkey():

448

print('Hotkey activated!')

449

450

hotkey = HotKey({Key.ctrl, Key.alt, KeyCode.from_char('h')}, on_hotkey)

451

452

def on_press(key):

453

hotkey.press(key)

454

455

def on_release(key):

456

hotkey.release(key)

457

458

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:

459

listener.join()

460

461

# Multiple global hotkeys

462

def copy_handler():

463

print('Copy hotkey pressed')

464

465

def paste_handler():

466

print('Paste hotkey pressed')

467

468

def quit_handler():

469

print('Quit hotkey pressed')

470

return False # Stop listener

471

472

hotkeys = {

473

'<ctrl>+c': copy_handler,

474

'<ctrl>+v': paste_handler,

475

'<ctrl>+<alt>+q': quit_handler

476

}

477

478

with GlobalHotKeys(hotkeys) as listener:

479

listener.join()

480

481

# Parse hotkey strings

482

try:

483

keys = HotKey.parse('<ctrl>+<shift>+a')

484

print(f'Parsed keys: {keys}')

485

except ValueError as e:

486

print(f'Invalid hotkey string: {e}')

487

```

488

489

## Types

490

491

### KeyCode Class

492

493

```python { .api }

494

class KeyCode:

495

"""Represents a key code used by the operating system."""

496

497

def __init__(self, vk: int = None, char: str = None, is_dead: bool = False):

498

"""

499

Initialize a KeyCode.

500

501

Args:

502

vk (int): Virtual key code

503

char (str): Character representation

504

is_dead (bool): Whether this is a dead key

505

"""

506

self.vk = vk

507

self.char = char

508

self.is_dead = is_dead

509

self.combining = None # Set for dead keys

510

511

@classmethod

512

def from_vk(cls, vk: int, **kwargs) -> 'KeyCode':

513

"""

514

Create a KeyCode from virtual key code.

515

516

Args:

517

vk (int): Virtual key code

518

**kwargs: Additional parameters

519

520

Returns:

521

KeyCode: New KeyCode instance

522

"""

523

...

524

525

@classmethod

526

def from_char(cls, char: str, **kwargs) -> 'KeyCode':

527

"""

528

Create a KeyCode from character.

529

530

Args:

531

char (str): Single character

532

**kwargs: Additional parameters

533

534

Returns:

535

KeyCode: New KeyCode instance

536

"""

537

...

538

539

@classmethod

540

def from_dead(cls, char: str, **kwargs) -> 'KeyCode':

541

"""

542

Create a dead key KeyCode.

543

544

Args:

545

char (str): Dead key character

546

**kwargs: Additional parameters

547

548

Returns:

549

KeyCode: New dead key KeyCode instance

550

"""

551

...

552

553

def join(self, key: 'KeyCode') -> 'KeyCode':

554

"""

555

Apply this dead key to another key.

556

557

Args:

558

key (KeyCode): Key to join with this dead key

559

560

Returns:

561

KeyCode: Combined key result

562

563

Raises:

564

ValueError: If keys cannot be joined

565

"""

566

...

567

```

568

569

### Key Enumeration

570

571

```python { .api }

572

class Key(enum.Enum):

573

"""Special keys that don't correspond to printable characters."""

574

575

# Modifier keys

576

alt # Generic Alt key

577

alt_l # Left Alt key

578

alt_r # Right Alt key

579

alt_gr # AltGr key

580

ctrl # Generic Ctrl key

581

ctrl_l # Left Ctrl key

582

ctrl_r # Right Ctrl key

583

shift # Generic Shift key

584

shift_l # Left Shift key

585

shift_r # Right Shift key

586

cmd # Generic Command/Windows key

587

cmd_l # Left Command/Windows key

588

cmd_r # Right Command/Windows key

589

590

# Navigation keys

591

up # Up arrow

592

down # Down arrow

593

left # Left arrow

594

right # Right arrow

595

home # Home key

596

end # End key

597

page_up # Page Up

598

page_down # Page Down

599

600

# Special keys

601

space # Spacebar

602

tab # Tab key

603

enter # Enter/Return key

604

esc # Escape key

605

backspace # Backspace key

606

delete # Delete key

607

608

# Lock keys

609

caps_lock # Caps Lock

610

num_lock # Num Lock

611

scroll_lock # Scroll Lock

612

613

# Function keys (F1-F20)

614

f1

615

f2

616

f3

617

f4

618

f5

619

f6

620

f7

621

f8

622

f9

623

f10

624

f11

625

f12

626

f13

627

f14

628

f15

629

f16

630

f17

631

f18

632

f19

633

f20

634

635

# Media keys

636

media_play_pause # Play/Pause toggle

637

media_volume_mute # Volume mute

638

media_volume_up # Volume up

639

media_volume_down # Volume down

640

media_previous # Previous track

641

media_next # Next track

642

643

# System keys (may be undefined on some platforms)

644

insert # Insert key

645

menu # Menu/Application key

646

pause # Pause/Break key

647

print_screen # Print Screen key

648

```

649

650

## Platform-Specific Features

651

652

### Windows (win32)

653

654

```python

655

# Custom event filtering

656

def win32_event_filter(msg, data):

657

"""Filter Windows keyboard events."""

658

# Access to KBDLLHOOKSTRUCT data

659

# Return False to suppress event from reaching listener

660

return True

661

662

listener = keyboard.Listener(

663

on_press=on_press,

664

win32_event_filter=win32_event_filter

665

)

666

```

667

668

### macOS (darwin)

669

670

```python

671

# Event intercepting and modification

672

def darwin_intercept(event_type, event):

673

"""Intercept and modify macOS keyboard events."""

674

# Modify event using Quartz.CGEvent functions

675

# Return None to suppress event system-wide

676

return event

677

678

listener = keyboard.Listener(

679

on_press=on_press,

680

darwin_intercept=darwin_intercept

681

)

682

```

683

684

### Linux (xorg)

685

686

Platform-specific options are available for X11/Xorg systems. The system requires access to the X server and proper DISPLAY environment variable configuration.

687

688

## Advanced Features

689

690

### Dead Key Support

691

692

Dead keys allow composition of accented characters:

693

694

```python

695

from pynput.keyboard import KeyCode

696

697

# Create dead key

698

dead_tilde = KeyCode.from_dead('~')

699

700

# Join with regular key

701

a_key = KeyCode.from_char('a')

702

result = dead_tilde.join(a_key) # Creates 'ã'

703

704

# Join with space or same key to get literal character

705

space_key = KeyCode.from_char(' ')

706

literal = dead_tilde.join(space_key) # Creates '~'

707

```

708

709

### Control Code Mapping

710

711

The keyboard module automatically maps control characters:

712

713

```python

714

# These are equivalent when typing

715

keyboard.type('\n') # Maps to Key.enter

716

keyboard.type('\t') # Maps to Key.tab

717

keyboard.type('\r') # Maps to Key.enter

718

```

719

720

## Error Handling

721

722

### Common Issues

723

724

- **ImportError**: Missing platform dependencies

725

- Windows: Requires win32 extensions

726

- macOS: Requires PyObjC framework bindings

727

- Linux: Requires python-xlib and X11 libraries

728

729

- **InvalidKeyException**: Invalid key parameters passed to controller methods

730

- **InvalidCharacterException**: Untypable characters in type() method

731

- **Permission Issues**: Some operations may require elevated privileges

732

733

### Exception Handling Examples

734

735

```python

736

from pynput.keyboard import Controller, Key

737

738

keyboard = Controller()

739

740

try:

741

keyboard.press('a')

742

keyboard.release('a')

743

except Controller.InvalidKeyException as e:

744

print(f"Invalid key: {e}")

745

746

try:

747

keyboard.type('Hello 🌍') # Emoji might not be typable

748

except Controller.InvalidCharacterException as e:

749

print(f"Cannot type character at position {e.args[0]}: {e.args[1]}")

750

751

# Hotkey parsing errors

752

try:

753

from pynput.keyboard import HotKey

754

keys = HotKey.parse('<invalid>+<key>')

755

except ValueError as e:

756

print(f"Invalid hotkey string: {e}")

757

```

758

759

### Listener Exception Handling

760

761

```python

762

from pynput import keyboard

763

764

def on_press(key):

765

# Raise StopException to gracefully stop listener

766

if key == keyboard.Key.esc:

767

raise keyboard.Listener.StopException()

768

769

def on_error():

770

print("Listener encountered an error")

771

772

try:

773

with keyboard.Listener(

774

on_press=on_press,

775

on_release=lambda key: None

776

) as listener:

777

listener.join()

778

except keyboard.Listener.StopException:

779

print("Listener stopped by user")

780

except Exception as e:

781

print(f"Listener error: {e}")

782

```