Skip to content

Commit 7bfec57

Browse files
authored
Fix terminal mode (#129)
* Don't crash when using 90° rotation * Fix another crash * Rotate characters * Run cargo fmt * Remove incorrect comment * Reset cursor on rotation * Handle display offset, clean up reset_pos * Remove outdated note * Remove outdated todo * Fix formatting * Remove outdated note from Builder * Add changelog entries * Fix things broken by rebase
1 parent 54ef093 commit 7bfec57

File tree

4 files changed

+67
-21
lines changed

4 files changed

+67
-21
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
### Changed
1515

16+
- **(breaking)** [#129](https://github.com/jamwaffles/ssd1306/pull/129) `TerminalMode::set_rotation` now resets the cursor position
1617
- **(breaking)** [#125](https://github.com/jamwaffles/ssd1306/pull/125) Redesigned display size handling.
1718
- **(breaking)** [#126](https://github.com/jamwaffles/ssd1306/pull/126) Moved `reset` method to `DisplayModeTrait`. If the prelude is not used, add either `use ssd1306::prelude::*` or `ssd1306::mode::displaymode::DisplayModeTrait` to your imports.
1819
- **(breaking)** [#119](https://github.com/jamwaffles/ssd1306/pull/119) Remove `DisplayMode` and `RawMode`
@@ -21,6 +22,10 @@
2122
- **(breaking)** [#116](https://github.com/jamwaffles/ssd1306/pull/116) Replace custom I2C and SPI interfaces by generic [`display-interface`](https://crates.io/crates/display-interface)
2223
- **(breaking)** [#113](https://github.com/jamwaffles/ssd1306/pull/113) Removed public `send_bounded_data` from DisplayInterface and implementations
2324

25+
### Fixed
26+
27+
- [#129](https://github.com/jamwaffles/ssd1306/pull/129) Fixed `Rotate90` and `Rotate270` rotation modes for `TerminalMode`
28+
2429
## [0.3.1] - 2020-03-21
2530

2631
### Fixed

src/builder.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,7 @@ where
113113
}
114114
}
115115

116-
/// Set the rotation of the display to one of four values. Defaults to no rotation. Note that
117-
/// 90º and 270º rotations are not supported by
118-
/// [`TerminalMode`](../mode/terminal/struct.TerminalMode.html).
116+
/// Set the rotation of the display to one of four values. Defaults to no rotation.
119117
pub fn with_rotation(self, rotation: DisplayRotation) -> Self {
120118
Self { rotation, ..self }
121119
}

src/displayrotation.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
//! Display rotation
22
3-
// TODO: Add to prelude
43
/// Display rotation.
5-
///
6-
/// Note that 90º and 270º rotations are not supported by
7-
// [`TerminalMode`](../mode/terminal/struct.TerminalMode.html).
84
#[derive(Clone, Copy)]
95
pub enum DisplayRotation {
106
/// No rotation, normal display

src/mode/terminal.rs

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
//! This mode uses the 7x7 pixel [MarioChrome](https://github.com/techninja/MarioChron/) font to
44
//! draw characters to the display without needing a framebuffer. It will write characters from top
55
//! left to bottom right in an 8x8 pixel grid, restarting at the top left of the display once full.
6-
//! The display itself takes care of wrapping lines.
76
//!
87
//! ```rust
98
//! # use ssd1306::test_helpers::I2cStub;
@@ -241,10 +240,18 @@ where
241240
self.ensure_cursor()?.set_position(0, cur_line);
242241
}
243242
_ => {
244-
// Send the pixel data to the display
245-
self.properties
246-
.draw(&Self::char_to_bitmap(c))
247-
.terminal_err()?;
243+
let bitmap = match self.properties.get_rotation() {
244+
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
245+
Self::char_to_bitmap(c)
246+
}
247+
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
248+
let bitmap = Self::char_to_bitmap(c);
249+
Self::rotate_bitmap(bitmap)
250+
}
251+
};
252+
253+
self.properties.draw(&bitmap).terminal_err()?;
254+
248255
// Increment character counter and potentially wrap line
249256
self.advance_cursor()?;
250257
}
@@ -265,9 +272,12 @@ where
265272
}
266273

267274
/// Set the display rotation
275+
///
276+
/// This method resets the cursor but does not clear the screen.
268277
pub fn set_rotation(&mut self, rot: DisplayRotation) -> Result<(), TerminalModeError> {
269-
// we don't need to touch the cursor because rotating 90º or 270º currently just flips
270-
self.properties.set_rotation(rot).terminal_err()
278+
self.properties.set_rotation(rot).terminal_err()?;
279+
// Need to reset cursor position, otherwise coordinates can become invalid
280+
self.reset_pos()
271281
}
272282

273283
/// Turn the display on or off. The display can be drawn to and retains all
@@ -298,29 +308,49 @@ where
298308
if column >= width || row >= height {
299309
Err(OutOfBounds)
300310
} else {
301-
self.properties.set_column(column * 8).terminal_err()?;
302-
self.properties.set_row(row * 8).terminal_err()?;
311+
match self.properties.get_rotation() {
312+
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
313+
self.properties
314+
.set_column(DSIZE::OFFSETX + column * 8)
315+
.terminal_err()?;
316+
self.properties
317+
.set_row(DSIZE::OFFSETY + row * 8)
318+
.terminal_err()?;
319+
}
320+
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
321+
self.properties
322+
.set_column(DSIZE::OFFSETX + row * 8)
323+
.terminal_err()?;
324+
self.properties
325+
.set_row(DSIZE::OFFSETY + column * 8)
326+
.terminal_err()?;
327+
}
328+
}
303329
self.ensure_cursor()?.set_position(column, row);
304330
Ok(())
305331
}
306332
}
307333

308334
/// Reset the draw area and move pointer to the top left corner
309335
fn reset_pos(&mut self) -> Result<(), TerminalModeError> {
310-
self.properties.set_column(DSIZE::OFFSETX).terminal_err()?;
311-
self.properties.set_row(DSIZE::OFFSETY).terminal_err()?;
312336
// Initialise the counter when we know it's valid
313337
self.cursor = Some(Cursor::new(DSIZE::WIDTH, DSIZE::HEIGHT));
314338

339+
// Reset cursor position
340+
self.set_position(0, 0)?;
341+
315342
Ok(())
316343
}
317344

318345
/// Advance the cursor, automatically wrapping lines and/or screens if necessary
319346
/// Takes in an already-unwrapped cursor to avoid re-unwrapping
320347
fn advance_cursor(&mut self) -> Result<(), TerminalModeError> {
321-
if let Some(CursorWrapEvent(new_row)) = self.ensure_cursor()?.advance() {
322-
self.properties.set_row(new_row * 8).terminal_err()?;
323-
}
348+
let cursor = self.ensure_cursor()?;
349+
350+
cursor.advance();
351+
let (c, r) = cursor.get_position();
352+
self.set_position(c, r)?;
353+
324354
Ok(())
325355
}
326356

@@ -427,6 +457,23 @@ where
427457
_ => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
428458
}
429459
}
460+
461+
fn rotate_bitmap(bitmap: [u8; 8]) -> [u8; 8] {
462+
let mut rotated: [u8; 8] = [0; 8];
463+
464+
for col in 0..8 {
465+
// source.msb is the top pixel
466+
let source = bitmap[col];
467+
for row in 0..8 {
468+
let bit = source & 1 << row != 0;
469+
if bit {
470+
rotated[row] |= 1 << col;
471+
}
472+
}
473+
}
474+
475+
rotated
476+
}
430477
}
431478

432479
impl<DI, DSIZE> fmt::Write for TerminalMode<DI, DSIZE>

0 commit comments

Comments
 (0)