summaryrefslogtreecommitdiffstats
path: root/src/lib.rs
blob: f849712f22b4eb7c5910f8bc66a024fa2ede14b2 (plain)
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
mod codes;

use std::fs::{File, OpenOptions};
use std::path::Path;

use std::io::BufWriter;
use std::io::Result;
use std::io::Write;

use codes::SpecialCode;
use codes::WriteInto;

/// A screen that allows you to send commands to a charlcd driver (or whatever
/// that implements the `Write` trait).
///
/// Nominal usage:
///
/// ```rust
/// use charlcd::Screen;
///
/// fn main() -> std::io::Result<()> {
///     let mut screen = Screen::default(); // will use "/dev/lcd" charlcd driver
///
///     screen.clear()?;
///     screen.write(b"hello, world!")?;
///     screen.flash_backlight()?;
///     screen.flush()?; // send all the previous commands to the driver at once
/// }
/// ```
pub struct Screen<T> {
    writer: T,
}

macro_rules! write_simple_code {
    ($self:expr, $code:expr) => {{
        $code.write_into(&mut $self.writer)?;
        Ok(())
    }};
}

impl<T> Screen<T>
where
    T: Write,
{
    /// Create a new display instance that will use the provided Writer under
    /// the hood to send commands.
    pub fn new(writer: T) -> Screen<T> {
        Screen { writer }
    }

    /// Clean the rest of the current line, from cursor's position.
    pub fn kill_eol(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::KillEndOfLine)
    }

    /// Reinitialize the display to its default values.
    pub fn reinit(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::ReinitializeDisplay)
    }

    /// Enable the display text output.
    pub fn display_on(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::DisplayOn)
    }

    /// Disable the display text output.
    pub fn display_off(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::DisplayOff)
    }

    /// Enable the underscore cursor (independent of blinking cursor).
    pub fn cursor_on(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::CursorOn)
    }

    /// Disable the underscore cursor (independent of blinking cursor).
    pub fn cursor_off(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::CursorOff)
    }

    /// Enable the blinking cursor (independent of underscore cursor).
    pub fn blink_on(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::BlinkOn)
    }

    /// Disable the blinking cursor (independent of underscore cursor).
    pub fn blink_off(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::BlinkOff)
    }

    /// Enable the backlight.
    pub fn backlight_on(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::BacklightOn)
    }

    /// Disable the backlight.
    pub fn backlight_off(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::BacklightOff)
    }

    /// Flash the backlight during a small duration.
    pub fn flash_backlight(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::FlashBacklight)
    }

    /// Clear the screen and return the cursor at original (0, 0) XY position.
    pub fn clear(&mut self) -> std::io::Result<()> {
        self.write(&[0x0c])?; // '\f' escape not defined in Rust
        Ok(())
    }

    /// Move the cursor back one character.
    pub fn back(&mut self) -> std::io::Result<()> {
        self.write(&[0x08])?; // '\b' escape not defined in Rust
        Ok(())
    }

    // Less-used (and some non-working?) methods below

    /// Shift cursor left.
    pub fn shift_cursor_left(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::ShiftCursorLeft)
    }

    /// Shift cursor right.
    pub fn shift_cursor_right(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::ShiftCursorRight)
    }

    /// Shift display left.
    pub fn shift_display_left(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::ShiftDisplayLeft)
    }

    /// Shift display right.
    pub fn shift_display_right(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::ShiftDisplayRight)
    }

    /// Enable one line mode.
    ///
    /// ![test](https://blog.microjoe.org/images/hd44780-lcd-i2c-screen-using-linux-mainline-charlcd-driver/linux_419.medium.jpg)
    pub fn one_line(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::OneLine)
    }

    /// Enable two lines mode.
    pub fn two_lines(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::TwoLines)
    }

    /// Enable small font mode.
    ///
    /// Seems to have no effect on the screen given the tests with multiple
    /// screen variants.
    pub fn small_font(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::SmallFont)
    }

    /// Enable big font mode.
    ///
    /// Seems to have no effect on the screen given the tests with multiple
    /// screen variants.
    pub fn large_font(&mut self) -> std::io::Result<()> {
        write_simple_code!(self, SpecialCode::LargeFont)
    }
}

// Reimplement Write trait for Screen, so that user can call the write and
// flush methods of the inner writer.
impl<T> Write for Screen<T>
where
    T: Write,
{
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.writer.write(buf)
    }
    fn flush(&mut self) -> Result<()> {
        self.writer.flush()
    }
}

// Concrete screen based on a File, to write to the real charlcd driver (or to
// another file).
type FileScreen = Screen<BufWriter<File>>;

const DEFAULT_SCREEN_DEV_PATH: &str = "/dev/lcd";

impl FileScreen {
    /// Create a Screen instance based on the passed path to the device.
    pub fn from_dev_path(path: &Path) -> std::io::Result<FileScreen> {
        let file = OpenOptions::new().write(true).open(path)?;
        let buf = BufWriter::new(file);
        Ok(Screen::new(buf))
    }

    /// Create a default Screen instance based on `"/dev/lcd"` path.
    pub fn default() -> std::io::Result<FileScreen> {
        Screen::from_dev_path(&Path::new(DEFAULT_SCREEN_DEV_PATH))
    }
}