or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-rendering.mdcore-playback.mdevent-handling.mdindex.mdinput-keybinding.mdplaylist-media.mdproperty-management.mdscreenshots-overlays.mdstreaming.md

input-keybinding.mddocs/

0

# Input and Key Binding

1

2

Input event handling, custom key bindings, mouse interaction, and OSD (On-Screen Display) control. Provides comprehensive control over user input and visual feedback systems.

3

4

## Capabilities

5

6

### Keyboard Input

7

8

Send keyboard events and manage key bindings for interactive control.

9

10

```python { .api }

11

def keypress(self, name: str):

12

"""

13

Send a key press event.

14

15

Parameters:

16

- name: Key name (e.g., 'SPACE', 'Enter', 'Esc', 'LEFT', 'a', 'ctrl+c')

17

"""

18

19

def keydown(self, name: str):

20

"""

21

Send a key down event (without release).

22

23

Parameters:

24

- name: Key name

25

"""

26

27

def keyup(self, name: str = None):

28

"""

29

Send a key up event.

30

31

Parameters:

32

- name: Key name (None for last key pressed)

33

"""

34

35

def keybind(self, name: str, command: str):

36

"""

37

Create a simple key binding to mpv command.

38

39

Parameters:

40

- name: Key combination

41

- command: mpv command string to execute

42

"""

43

```

44

45

### Advanced Key Binding

46

47

Register Python callbacks for key events with flexible binding modes.

48

49

```python { .api }

50

def register_key_binding(self, keydef: str, callback_or_cmd, mode: str = 'force'):

51

"""

52

Register a key binding with callback or command.

53

54

Parameters:

55

- keydef: Key definition string

56

- callback_or_cmd: Python function or mpv command string

57

- mode: Binding mode ('force', 'weak')

58

'force': Override existing bindings

59

'weak': Only bind if no existing binding

60

"""

61

62

def unregister_key_binding(self, keydef: str):

63

"""

64

Remove a key binding.

65

66

Parameters:

67

- keydef: Key definition to remove

68

"""

69

70

def key_binding(self, keydef: str, mode: str = 'force'):

71

"""

72

Decorator for registering key binding callbacks.

73

74

Parameters:

75

- keydef: Key definition string

76

- mode: Binding mode ('force', 'weak')

77

78

Returns:

79

Decorator function for callback registration

80

"""

81

82

def on_key_press(self, keydef: str, mode: str = 'force', repetition: bool = False):

83

"""

84

Decorator for key press event handling.

85

86

Parameters:

87

- keydef: Key definition string

88

- mode: Binding mode ('force', 'weak')

89

- repetition: Whether to handle key repetition

90

91

Returns:

92

Decorator function for callback registration

93

"""

94

```

95

96

### Mouse Input

97

98

Handle mouse events including clicks, movement, and scroll wheel.

99

100

```python { .api }

101

def mouse(self, x: int, y: int, button: int = None, mode: str = 'single'):

102

"""

103

Send mouse event.

104

105

Parameters:

106

- x, y: Mouse coordinates

107

- button: Mouse button (0=left, 1=middle, 2=right)

108

- mode: Click mode ('single', 'double')

109

"""

110

```

111

112

### On-Screen Display (OSD)

113

114

Control mpv's built-in OSD for displaying information and status.

115

116

```python { .api }

117

def toggle_osd(self):

118

"""Toggle OSD visibility through different states."""

119

120

def print_text(self, text: str):

121

"""

122

Print text to terminal/console.

123

124

Parameters:

125

- text: Text to print

126

"""

127

128

def show_text(self, string: str, duration: str = '-1', level: int = 0):

129

"""

130

Display text on the OSD.

131

132

Parameters:

133

- string: Text to display (supports property expansion)

134

- duration: Display duration in milliseconds ('-1' for default)

135

- level: OSD level (0=subtitles, 1=seek bar, 2=always visible)

136

"""

137

138

def show_progress(self):

139

"""Show progress bar on OSD."""

140

```

141

142

### Text Processing

143

144

Process text templates with property expansion for dynamic content.

145

146

```python { .api }

147

def expand_text(self, text: str) -> str:

148

"""

149

Expand text template with property values.

150

151

Parameters:

152

- text: Template text with ${property} placeholders

153

154

Returns:

155

Expanded text with property values substituted

156

"""

157

158

def expand_path(self, path: str) -> str:

159

"""

160

Expand path template with mpv path expansion.

161

162

Parameters:

163

- path: Path template

164

165

Returns:

166

Expanded path string

167

"""

168

```

169

170

## Key Definition Format

171

172

Key definitions use mpv's key naming convention:

173

174

### Basic Keys

175

- Letter keys: `a`, `b`, `c`, etc.

176

- Number keys: `0`, `1`, `2`, etc.

177

- Function keys: `F1`, `F2`, `F3`, etc.

178

- Arrow keys: `LEFT`, `RIGHT`, `UP`, `DOWN`

179

- Special keys: `SPACE`, `Enter`, `Esc`, `Tab`, `Backspace`

180

181

### Modifier Keys

182

- `ctrl+key`: Control modifier

183

- `alt+key`: Alt modifier

184

- `shift+key`: Shift modifier

185

- `meta+key`: Meta/Windows modifier

186

187

### Mouse Events

188

- `MOUSE_BTN0`: Left mouse button

189

- `MOUSE_BTN1`: Middle mouse button

190

- `MOUSE_BTN2`: Right mouse button

191

- `WHEEL_UP`, `WHEEL_DOWN`: Mouse wheel

192

193

### Combined Modifiers

194

- `ctrl+alt+key`: Multiple modifiers

195

- `shift+ctrl+F1`: Function key with modifiers

196

197

## Usage Examples

198

199

### Basic Key Bindings

200

201

```python

202

import mpv

203

204

player = mpv.MPV()

205

206

# Simple command bindings

207

player.keybind('SPACE', 'cycle pause')

208

player.keybind('f', 'cycle fullscreen')

209

player.keybind('m', 'cycle mute')

210

player.keybind('LEFT', 'seek -10')

211

player.keybind('RIGHT', 'seek 10')

212

213

# Volume control

214

player.keybind('UP', 'add volume 5')

215

player.keybind('DOWN', 'add volume -5')

216

217

# Subtitle controls

218

player.keybind('s', 'cycle sub-visibility')

219

player.keybind('j', 'cycle sub')

220

```

221

222

### Python Callback Bindings

223

224

```python

225

# Using decorator

226

@player.key_binding('q')

227

def quit_handler():

228

print("Quit requested")

229

player.quit()

230

231

@player.key_binding('i')

232

def info_handler():

233

print(f"Playing: {player.filename}")

234

print(f"Position: {player.time_pos}/{player.duration}")

235

player.show_text(f"Time: ${time-pos}/${duration}")

236

237

# Using method registration

238

def screenshot_handler():

239

filename = f"screenshot_{int(player.time_pos)}.png"

240

player.screenshot_to_file(filename)

241

player.show_text(f"Screenshot saved: {filename}", duration='2000')

242

243

player.register_key_binding('p', screenshot_handler)

244

245

# Advanced callback with key info

246

def debug_key_handler():

247

pos = player.time_pos or 0

248

vol = player.volume

249

paused = player.pause

250

251

debug_info = f"Pos:{pos:.1f} Vol:{vol} Paused:{paused}"

252

print(debug_info)

253

player.show_text(debug_info, level=1)

254

255

player.register_key_binding('d', debug_key_handler)

256

```

257

258

### Mouse Interaction

259

260

```python

261

# Mouse click handlers

262

def handle_mouse_click():

263

# Toggle pause on mouse click

264

player.pause = not player.pause

265

266

player.register_key_binding('MOUSE_BTN0', handle_mouse_click)

267

268

# Mouse wheel for volume

269

def wheel_up():

270

player.property_add('volume', 5)

271

player.show_text(f"Volume: ${volume}")

272

273

def wheel_down():

274

player.property_add('volume', -5)

275

player.show_text(f"Volume: ${volume}")

276

277

player.register_key_binding('WHEEL_UP', wheel_up)

278

player.register_key_binding('WHEEL_DOWN', wheel_down)

279

280

# Programmatic mouse events

281

def click_center():

282

width = player.width or 800

283

height = player.height or 600

284

player.mouse(width // 2, height // 2, button=0)

285

286

# Double-click for fullscreen

287

def double_click_fullscreen():

288

player.mouse(400, 300, mode='double')

289

player.fullscreen = not player.fullscreen

290

291

player.register_key_binding('ctrl+MOUSE_BTN0', double_click_fullscreen)

292

```

293

294

### Advanced Key Binding

295

296

```python

297

# Context-sensitive bindings

298

def smart_seek():

299

"""Smart seeking based on current position."""

300

pos = player.time_pos or 0

301

duration = player.duration or 1

302

303

# Larger jumps near beginning/end

304

if pos < duration * 0.1 or pos > duration * 0.9:

305

seek_amount = 30

306

else:

307

seek_amount = 10

308

309

player.seek(seek_amount)

310

player.show_text(f"Seek +{seek_amount}s")

311

312

player.register_key_binding('ctrl+RIGHT', smart_seek)

313

314

# Multi-key sequences (custom implementation)

315

class MultiKeyHandler:

316

def __init__(self, player):

317

self.player = player

318

self.sequence = []

319

self.timeout = None

320

321

def handle_key(self, key):

322

self.sequence.append(key)

323

324

# Check for known sequences

325

seq_str = ''.join(self.sequence)

326

if seq_str == 'gg': # Go to beginning

327

self.player.seek(0, reference='absolute')

328

self.reset()

329

elif seq_str == 'GG': # Go to end

330

self.player.seek(100, reference='percent')

331

self.reset()

332

elif len(self.sequence) >= 2:

333

self.reset()

334

335

def reset(self):

336

self.sequence = []

337

338

multi_key = MultiKeyHandler(player)

339

340

@player.key_binding('g')

341

def handle_g():

342

multi_key.handle_key('g')

343

344

@player.key_binding('G')

345

def handle_G():

346

multi_key.handle_key('G')

347

```

348

349

### OSD and Text Display

350

351

```python

352

# Property-based text display

353

@player.key_binding('t')

354

def show_time():

355

player.show_text("Time: ${time-pos} / ${duration}")

356

357

@player.key_binding('v')

358

def show_volume():

359

player.show_text("Volume: ${volume}%", duration='1500')

360

361

@player.key_binding('r')

362

def show_resolution():

363

text = "Resolution: ${width}x${height} @ ${fps} FPS"

364

player.show_text(text, level=1)

365

366

# Custom OSD formatting

367

def format_time_display():

368

pos = player.time_pos or 0

369

dur = player.duration or 0

370

371

pos_min, pos_sec = divmod(int(pos), 60)

372

dur_min, dur_sec = divmod(int(dur), 60)

373

374

time_str = f"{pos_min:02d}:{pos_sec:02d} / {dur_min:02d}:{dur_sec:02d}"

375

return time_str

376

377

@player.key_binding('T')

378

def show_formatted_time():

379

time_display = format_time_display()

380

player.show_text(time_display, duration='2000', level=2)

381

382

# Progress indicator

383

@player.key_binding('P')

384

def show_progress_info():

385

player.show_progress()

386

387

# Additional info

388

percent = player.percent_pos or 0

389

player.show_text(f"Progress: {percent:.1f}%", level=1)

390

```

391

392

### Interactive Controls

393

394

```python

395

class InteractivePlayer:

396

def __init__(self):

397

self.player = mpv.MPV()

398

self.help_visible = False

399

self.setup_bindings()

400

401

def setup_bindings(self):

402

"""Setup all key bindings."""

403

404

# Help system

405

@self.player.key_binding('h')

406

def toggle_help():

407

self.toggle_help_display()

408

409

# Playback controls

410

@self.player.key_binding('SPACE')

411

def toggle_pause():

412

self.player.pause = not self.player.pause

413

state = "Paused" if self.player.pause else "Playing"

414

self.player.show_text(state, duration='1000')

415

416

# Seeking with feedback

417

@self.player.key_binding('l')

418

def seek_forward():

419

self.player.seek(10)

420

pos = self.player.time_pos or 0

421

self.player.show_text(f"→ {pos:.1f}s", duration='800')

422

423

@self.player.key_binding('j')

424

def seek_backward():

425

self.player.seek(-10)

426

pos = self.player.time_pos or 0

427

self.player.show_text(f"← {pos:.1f}s", duration='800')

428

429

# Volume with visual feedback

430

@self.player.key_binding('+')

431

def volume_up():

432

self.player.property_add('volume', 5)

433

vol = self.player.volume

434

self.player.show_text(f"Volume: {vol:.0f}%", duration='1000')

435

436

@self.player.key_binding('-')

437

def volume_down():

438

self.player.property_add('volume', -5)

439

vol = self.player.volume

440

self.player.show_text(f"Volume: {vol:.0f}%", duration='1000')

441

442

def toggle_help_display(self):

443

"""Toggle help text display."""

444

if self.help_visible:

445

self.player.show_text("", duration='0') # Clear text

446

self.help_visible = False

447

else:

448

help_text = """

449

Key Bindings:

450

SPACE - Play/Pause h - Toggle Help

451

l/j - Seek ±10s +/- - Volume ±5

452

f - Fullscreen q - Quit

453

""".strip()

454

self.player.show_text(help_text, duration='-1', level=2)

455

self.help_visible = True

456

457

# Usage

458

interactive_player = InteractivePlayer()

459

interactive_player.player.play('/path/to/video.mp4')

460

```

461

462

### Dynamic Key Binding

463

464

```python

465

class DynamicControls:

466

def __init__(self, player):

467

self.player = player

468

self.mode = 'normal'

469

self.setup_mode_switching()

470

471

def setup_mode_switching(self):

472

"""Setup mode-based key bindings."""

473

474

@self.player.key_binding('ESC')

475

def exit_mode():

476

self.set_mode('normal')

477

478

@self.player.key_binding('1')

479

def number_mode():

480

self.set_mode('numbers')

481

482

@self.player.key_binding('2')

483

def seek_mode():

484

self.set_mode('seeking')

485

486

def set_mode(self, mode):

487

"""Change control mode and update bindings."""

488

self.mode = mode

489

490

# Clear existing dynamic bindings

491

for key in ['a', 'b', 'c', 'd']:

492

try:

493

self.player.unregister_key_binding(key)

494

except:

495

pass

496

497

# Setup mode-specific bindings

498

if mode == 'numbers':

499

self.setup_number_mode()

500

elif mode == 'seeking':

501

self.setup_seek_mode()

502

503

self.player.show_text(f"Mode: {mode}", duration='1500')

504

505

def setup_number_mode(self):

506

"""Number input mode bindings."""

507

def make_number_handler(num):

508

def handler():

509

self.player.show_text(f"Number: {num}")

510

return handler

511

512

for i in range(10):

513

self.player.register_key_binding(

514

str(i), make_number_handler(i))

515

516

def setup_seek_mode(self):

517

"""Seek mode bindings."""

518

@self.player.key_binding('a')

519

def seek_10():

520

self.player.seek(10)

521

522

@self.player.key_binding('b')

523

def seek_60():

524

self.player.seek(60)

525

526

@self.player.key_binding('c')

527

def seek_300():

528

self.player.seek(300)

529

530

# Usage

531

dynamic = DynamicControls(player)

532

```