mousefood 0.5.2

embedded-graphics backend for Ratatui
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# ![Mousefood]https://github.com/ratatui/mousefood/blob/599f1026d37c8d6308a6df64a234dbefaedc0c6f/assets/logo/mousefood.svg?raw=true

[![Crate](https://img.shields.io/crates/v/mousefood?logo=rust&style=flat-square&color=ebe94f)](https://crates.io/crates/mousefood)
[![Docs](https://img.shields.io/docsrs/mousefood?logo=rust&style=flat-square)](https://docs.rs/mousefood)
[![CI](https://img.shields.io/github/actions/workflow/status/ratatui/mousefood/ci.yml?style=flat-square&logo=github)](https://github.com/ratatui/mousefood/blob/main/.github/workflows/ci.yml)
[![Deps](https://deps.rs/crate/mousefood/latest/status.svg?style=flat-square)](https://deps.rs/crate/mousefood)

**Mousefood** - a no-std
[embedded-graphics](https://crates.io/crates/embedded-graphics) backend
for [Ratatui](https://crates.io/crates/ratatui)!

<div align="center">

![demo](https://github.com/ratatui/mousefood/blob/599f1026d37c8d6308a6df64a234dbefaedc0c6f/assets/demo.jpg?raw=true)
![animated demo](https://github.com/ratatui/mousefood/blob/599f1026d37c8d6308a6df64a234dbefaedc0c6f/assets/demo.gif?raw=true)

</div>

## Quickstart

Add mousefood as a dependency:

```shell
cargo add mousefood
```

Exemplary setup:

```rust
use mousefood::embedded_graphics::{mock_display::MockDisplay, pixelcolor::Rgb888};
use mousefood::prelude::*;
use ratatui::widgets::{Block, Paragraph};
use ratatui::{Frame, Terminal};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // replace this with your display driver
    // e.g. ILI9341, ST7735, SSD1306, etc.
    let mut display = MockDisplay::<Rgb888>::new();

    let backend = EmbeddedBackend::new(&mut display, EmbeddedBackendConfig::default());
    let mut terminal = Terminal::new(backend)?;

    terminal.draw(draw)?;
    Ok(())
}

fn draw(frame: &mut Frame) {
    let block = Block::bordered().title("Mousefood");
    let paragraph = Paragraph::new("Hello from Mousefood!").block(block);
    frame.render_widget(paragraph, frame.area());
}
```

### Special characters

Embedded-graphics includes bitmap fonts that have a very limited
set of characters to save space (ASCII, ISO 8859 or JIS X0201).
This makes it impossible to draw most of Ratatui's widgets,
which heavily use box-drawing glyphs, Braille,
and other special characters.

Mousefood by default uses [`embedded-graphics-unicodefonts`](https://crates.io/crates/embedded-graphics-unicodefonts),
which provides embedded-graphics fonts with a much larger set of characters.

#### Alternatives

In order to save space and [speed up rendering](#performance-and-hardware-support),
the `fonts` feature can be disabled by turning off the default crate features.
[`ibm437`](https://crates.io/crates/ibm437) is a good alternative that includes
some drawing characters, but is not as large as embedded-graphics-unicodefonts.

### Bold and italic fonts

Bold and italic modifiers are supported, but this requires providing fonts
through `EmbeddedBackendConfig`.
If only regular font is provided, it serves as a fallback.
All fonts must be of the same size.

```rust
use mousefood::embedded_graphics::{mock_display::MockDisplay, pixelcolor::Rgb888};
use mousefood::{EmbeddedBackend, EmbeddedBackendConfig, fonts};
use ratatui::Terminal;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut display = MockDisplay::<Rgb888>::new();
    let config = EmbeddedBackendConfig {
        font_regular: fonts::MONO_6X13,
        font_bold: Some(fonts::MONO_6X13_BOLD),
        font_italic: Some(fonts::MONO_6X13_ITALIC),
        ..Default::default()
    };
    let backend = EmbeddedBackend::new(&mut display, config);
    let _terminal = Terminal::new(backend)?;
    Ok(())
}
```

<div align="center">
<img alt="Bold and Italic fonts"
     src="https://github.com/ratatui/mousefood/blob/6640da9402794ea8f9370e0dc2b4bd1ebf2c6356/assets/bold_italic.png?raw=true"
     style="max-width: 640px"/>
</div>

### Color theme

Colors can be remapped using `color_theme` on `EmbeddedBackendConfig`.
By default the ANSI palette is used.

```rust
use mousefood::{ColorTheme, EmbeddedBackend, EmbeddedBackendConfig};
use mousefood::embedded_graphics::{mock_display::MockDisplay, pixelcolor::Rgb888};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut display = MockDisplay::<Rgb888>::new();
    let theme = ColorTheme {
        background: Rgb888::new(5, 5, 5),
        foreground: Rgb888::new(240, 240, 240),
        yellow: Rgb888::new(255, 200, 0),
        ..ColorTheme::ansi()
    };

    let config = EmbeddedBackendConfig {
        color_theme: theme,
        ..Default::default()
    };
    let backend = EmbeddedBackend::new(&mut display, config);
    Ok(())
}
```

#### Built-in themes

Mousefood includes popular color themes that can be used directly:

- `ColorTheme::ansi()` - Standard ANSI colors (default)
- `ColorTheme::tokyo_night()` - Tokyo Night dark theme with blue/purple tones

### Cursor and blink

Mousefood supports configurable cursor styles and text blinking.

The cursor style can be set to `Inverse` (default), `Underline`, `Outline`, or `Japanese`.
Inverse mode requires the `framebuffer` feature and falls back to underline without it.

```rust,ignore
let config = EmbeddedBackendConfig {
    cursor: CursorConfig {
        style: CursorStyle::Japanese,
        blink: true,
        color: Rgb888::WHITE,

    },
    ..Default::default()
};
```

Text blink modifiers (`SLOW_BLINK`, `RAPID_BLINK`) and cursor blinking are
behind the `blink` feature flag to avoid unnecessary computation and memory
usage when not needed:

```toml
[dependencies]
mousefood = { version = "*", features = ["blink"] }
```

Blink timing is configurable:

```rust,ignore
let config = EmbeddedBackendConfig {
    #[cfg(feature = "blink")]
    blink: BlinkConfig {
        fps: 30,
        slow: BlinkTiming { blinks_per_sec: 1, duty_percent: 15 },
        fast: BlinkTiming { blinks_per_sec: 3, duty_percent: 50 },
    },
    ..Default::default()
};
```

Without the `blink` feature, blink modifiers are silently ignored and the
cursor is always visible.

### Simulator

Mousefood can be run in a simulator using
[embedded-graphics-simulator](https://crates.io/crates/embedded-graphics-simulator) crate.

![GIF of a window running the simulator with a mousefood application](assets/blink.gif)

Run simulator example:

```shell
git clone https://github.com/ratatui/mousefood.git
cd mousefood/examples/simulator
cargo run
```

For more details, view the [simulator example](examples/simulator).

### EPD support

#### WeAct Studio

<div align="center">

![WeAct epd demo](https://github.com/ratatui/mousefood/blob/fa70cdd46567a51895caf10c44fff4104602e880/assets/epd-weact.jpg?raw=true)

</div>

Support for EPD (e-ink displays) produced by WeAct Studio
(`weact-studio-epd` driver) can be enabled using `epd-weact` feature.

This driver requires some additional configuration.
Follow the [`weact-studio-epd`](https://docs.rs/weact-studio-epd)
crate docs and apply the same `flush_callback` pattern used in the [Waveshare example below](#waveshare).

<details>
  <summary>Setup example</summary>

EPD drivers include their own internal buffers, so the mousefood framebuffer
adds memory overhead with no benefit. Disable default features to turn it off:

```toml
[dependencies]
mousefood = { version = "*", default-features = false, features = ["epd-weact"] }
```

```rust,ignore
use mousefood::prelude::*;
use weact_studio_epd::graphics::Display290BlackWhite;
use weact_studio_epd::WeActStudio290BlackWhiteDriver;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configure SPI + GPIO + delay provider for your board.
    // let (spi_interface, busy, rst, delay) = ...;

    let mut driver = WeActStudio290BlackWhiteDriver::new(spi_interface, busy, rst, delay);
    let mut display = Display290BlackWhite::new();

    driver.init()?;

    let config = EmbeddedBackendConfig {
        flush_callback: Box::new(move |d| {
            driver.full_update(d).expect("epd update failed");
        }),
        ..Default::default()
    };

    let backend = EmbeddedBackend::new(&mut display, config);
    let _terminal = Terminal::new(backend)?;
    Ok(())
}
```

</details>

#### Waveshare

Support for EPD (e-ink displays) produced by Waveshare Electronics
(`epd-waveshare` driver) can be enabled using `epd-waveshare` feature.

<details>
  <summary>Setup example</summary>

EPD drivers include their own internal buffers, so the mousefood framebuffer
adds memory overhead with no benefit. Disable default features to turn it off:

```toml
[dependencies]
mousefood = { version = "*", default-features = false, features = ["epd-waveshare"] }
```

```rust,ignore
use mousefood::prelude::*;
use epd_waveshare::{epd2in9_v2::*, prelude::*};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configure SPI + GPIO + delay provider for your board.
    // let (mut spi_device, busy, dc, rst, mut delay) = ...;

    let mut epd = Epd2in9::new(&mut spi_device, busy, dc, rst, &mut delay, None)?;
    let mut display = Display2in9::default();

    let config = EmbeddedBackendConfig {
        flush_callback: Box::new(move |d| {
            epd.update_and_display_frame(&mut spi_device, d.buffer(), &mut delay)
                .expect("epd update failed");
        }),
        ..Default::default()
    };

    let backend = EmbeddedBackend::new(&mut display, config);
    let _terminal = Terminal::new(backend)?;
    Ok(())
}
```

</details>

See the full embedded example at [`examples/epd-waveshare-demo`](https://github.com/ratatui/mousefood/tree/main/examples/epd-waveshare-demo).

#### Lilygo T5 e-paper

Support for the lilygo T5 e-paper produced by Lilygo with an esp32s3 and the EDO47TC1 panel driver.
(`lilygo-epd47` driver) can be enabled using `lilygo-epd47` feature.

<details>
  <summary>Setup example</summary>

The lilygo-epd47 driver include its own internal buffers, so the mousefood framebuffer
adds memory overhead with no benefit. Disable default features to turn it off:

```toml
[dependencies]
mousefood = { version = "*", default-features = false, features = ["lilygo-epd47"] }
```

```rust,ignore
use mousefood::prelude::*;

#[main]
fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Setup all that is required for your board and get the peripherals.
    // let peripherals = ...;

    let mut display = Display::new(
        pin_config!(peripherals),
        peripherals.DMA_CH0,
        peripherals.LCD_CAM,
        peripherals.RMT,
    )?;

    let theme = ColorTheme {
        background: Rgb888::WHITE,
        foreground: Rgb888::BLACK,
        ..ColorTheme::ansi()
    };

    // setup mousefood
    let backend = EmbeddedBackendConfig {
        color_theme: theme,
        font_regular: mousefood::fonts::mono_10x20_atlas(),
        flush_callback: Box::new(move |display: &mut Display| {
            display
                .flush(DrawMode::BlackOnWhite)
                .expect("to flush to the display")
        }),
        ..Default::default()
    };

    let backend = EmbeddedBackend::new(&mut display, backend);
    let _terminal = Terminal::new(backend)?;
    Ok(())
}
```

</details>

See the full embedded example at [`examples/lilygo-epd47-demo`](https://github.com/ratatui/mousefood/tree/main/examples/lilygo-epd47-demo).

## Performance and hardware support

Flash memory on most embedded devices is very limited. Additionally,
to achieve high frame rate when using the `fonts` feature,
it is recommended to use `opt-level = 3`,
which can make the resulting binary even larger.

Mousefood is hardware-agnostic.
Successfully tested on:

### Microcontrollers

- ESP32 (Xtensa)
- ESP32-S3 (Xtensa)
- ESP32-C6 (RISC-V)
- STM32
- RP2040
- RP2350

### Display drivers

For every driver, the list of displays is not exhaustive.

- [ssd1306]https://crates.io/crates/ssd1306 for SSD1306
- [mipidsi]https://crates.io/crates/mipidsi for ILI9341, ST7735, etc.
- [epd-waveshare]https://crates.io/crates/epd-waveshare for e-paper displays from Waveshare
  (requires enabling `epd-waveshare` feature)
- [weact-studio-epd]https://crates.io/crates/weact-studio-epd for e-paper displays
  from WeAct Studio (requires enabling `epd-weact` feature)
- [lilygo-epd47]https://crates.io/crates/lilygo-epd47 for the Lilygo T5 e-paper
  from Lilygo (requires enabling `lilygo-epd47` feature)

Send a pull request to add your microcontroller or display driver here!

## Docs

Full API docs are available on [docs.rs](https://docs.rs/mousefood).

## Contributing

All contributions are welcome!

Before opening a pull request, please read the [contributing guidelines](./CONTRIBUTING.md).

## Built with Mousefood

Here are some projects built using Mousefood:

- [AirSniffer]https://github.com/nebelgrau77/airsniffer-esp32c6 - Get information about your indoor
  climate at a glance.
- [Tuitar]https://github.com/orhun/tuitar - A portable guitar training tool.
- [Mnyaoo32]https://github.com/intuis/mnyaoo32 - An eccentric way to consume IRC messages using ESP32.
- [Phone-OS]https://github.com/Julien-cpsn/Phone-OS - A modern phone OS for ESP32 CYD.
- [MaraTUI]https://github.com/ololonly/maratui - A terminal dashboard for keeping
  an eye on Lelit Mara X espresso shots.

Send a pull request to add your project here!

## License

[![License MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square&color=8d97b3)](LICENSE-MIT)
[![License Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square&color=8d97b3)](LICENSE-APACHE)

Mousefood is dual-licensed under
[Apache 2.0](LICENSE-APACHE) and [MIT](LICENSE-MIT) terms.