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
pub mod common;
pub mod mxcfb;
pub mod screeninfo;

#[cfg(feature = "framebuffer-storage")]
pub mod storage;

#[cfg(feature = "framebuffer")]
pub mod io;

#[cfg(feature = "framebuffer")]
pub mod swtfb_client;

pub use cgmath;

pub trait FramebufferIO {
    /// Writes an arbitrary length frame into the framebuffer
    fn write_frame(&mut self, frame: &[u8]);
    /// Writes a single pixel at `pos` with value `v`
    fn write_pixel(&mut self, pos: cgmath::Point2<i32>, v: common::color);
    /// Reads the value of the pixel at `pos`
    fn read_pixel(&self, pos: cgmath::Point2<u32>) -> common::color;
    /// Reads the value at offset `ofst` from the mmapp'ed framebuffer region
    fn read_offset(&self, ofst: isize) -> u8;
    /// Dumps the contents of the specified rectangle into a `Vec<u8>` from which
    /// you can later create a CompressedCanvasState or pass to restore_region().
    /// The pixel format is rgb565_le.
    fn dump_region(&self, rect: common::mxcfb_rect) -> Result<Vec<u8>, &'static str>;
    /// Restores into the framebuffer the contents of the specified rectangle from a u8 slice
    fn restore_region(
        &mut self,
        rect: common::mxcfb_rect,
        data: &[u8],
    ) -> Result<u32, &'static str>;
}

#[cfg(feature = "framebuffer-drawing")]
mod graphics;

#[cfg(feature = "framebuffer-drawing")]
pub mod draw;

#[cfg(feature = "framebuffer-drawing")]
pub trait FramebufferDraw {
    #[cfg(feature = "image")]
    /// Draws `img` at `pos` with 1:1 scaling
    fn draw_image(&mut self, img: &image::RgbImage, pos: cgmath::Point2<i32>)
        -> common::mxcfb_rect;
    /// Draws a straight line
    fn draw_line(
        &mut self,
        start: cgmath::Point2<i32>,
        end: cgmath::Point2<i32>,
        width: u32,
        v: common::color,
    ) -> common::mxcfb_rect;
    /// Draws a circle using Bresenham circle algorithm
    fn draw_circle(
        &mut self,
        pos: cgmath::Point2<i32>,
        rad: u32,
        c: common::color,
    ) -> common::mxcfb_rect;
    /// Fills a circle
    fn fill_circle(
        &mut self,
        pos: cgmath::Point2<i32>,
        rad: u32,
        c: common::color,
    ) -> common::mxcfb_rect;
    /// Draws a polygon
    fn draw_polygon(
        &mut self,
        _: &[cgmath::Point2<i32>],
        fill: bool,
        c: common::color,
    ) -> common::mxcfb_rect;
    /// Draws a bezier curve begining at `startpt`, with control point `ctrlpt`, ending at `endpt` with `color`
    fn draw_bezier(
        &mut self,
        startpt: cgmath::Point2<f32>,
        ctrlpt: cgmath::Point2<f32>,
        endpt: cgmath::Point2<f32>,
        width: f32,
        samples: i32,
        v: common::color,
    ) -> common::mxcfb_rect;
    /// Draws a bezier curve begining at `startpt`, with control point `ctrlpt`, ending at `endpt`
    /// with a width at each point and color `color`
    fn draw_dynamic_bezier(
        &mut self,
        startpt: (cgmath::Point2<f32>, f32),
        ctrlpt: (cgmath::Point2<f32>, f32),
        endpt: (cgmath::Point2<f32>, f32),
        samples: i32,
        v: common::color,
    ) -> common::mxcfb_rect;
    /// Draws `text` at `pos` with `color` using scale `size`
    #[cfg(feature = "framebuffer-text-drawing")]
    fn draw_text(
        &mut self,
        pos: cgmath::Point2<f32>,
        text: &str,
        size: f32,
        col: common::color,
        dryrun: bool,
    ) -> common::mxcfb_rect;
    /// Draws a 1px border rectangle of size `size` at `pos` with `border_px` border thickness
    fn draw_rect(
        &mut self,
        pos: cgmath::Point2<i32>,
        size: cgmath::Vector2<u32>,
        border_px: u32,
        c: common::color,
    );
    /// Fills rectangle of size `size` at `pos`
    fn fill_rect(&mut self, pos: cgmath::Point2<i32>, size: cgmath::Vector2<u32>, c: common::color);
    /// Clears the framebuffer however does not perform a refresh
    fn clear(&mut self);
}

#[cfg(feature = "framebuffer")]
pub mod core;
pub trait FramebufferBase {
    /// Toggles the EPD Controller (see https://wiki.mobileread.com/wiki/EPD_controller)
    fn set_epdc_access(&mut self, state: bool);
    /// Toggles autoupdate mode
    fn set_autoupdate_mode(&mut self, mode: u32);
    /// Toggles update scheme
    fn set_update_scheme(&mut self, scheme: u32);
    /// Creates a FixScreeninfo struct and fills it using ioctl
    fn get_fix_screeninfo(device: &std::fs::File) -> screeninfo::FixScreeninfo;
    /// Creates a VarScreeninfo struct and fills it using ioctl
    fn get_var_screeninfo(device: &std::fs::File) -> screeninfo::VarScreeninfo;
    /// Makes the proper ioctl call to set the VarScreenInfo.
    /// You must first update the contents of self.var_screen_info
    /// and then call this function.
    fn put_var_screeninfo(
        device: &std::fs::File,
        var_screen_info: &mut screeninfo::VarScreeninfo,
    ) -> bool;

    fn update_var_screeninfo(&mut self) -> bool;
}

pub enum PartialRefreshMode {
    DryRun,
    Async,
    Wait,
}

#[cfg(feature = "framebuffer")]
pub mod refresh;
pub trait FramebufferRefresh {
    /// Refreshes the entire screen with the provided parameters. If `wait_completion` is
    /// set to true, doesn't return before the refresh has been completed. Returns the marker.
    fn full_refresh(
        &self,
        waveform_mode: common::waveform_mode,
        temperature: common::display_temp,
        dither_mode: common::dither_mode,
        quant_bit: i32,
        wait_completion: bool,
    ) -> u32;

    /// Refreshes the given `region` with the provided parameters. If `mode` is `DryRun` or
    /// `Wait`, this function won't return before the `DryRun`'s collision_test or
    /// refresh has been completed. In `Async` mode, this function will return immediately
    /// and return a `marker` which can then later be fed to `wait_refresh_complete` to wait
    /// for its completion. In `DryRun`, it will return the `collision_test` result.
    ///
    /// `force_full_refresh` allows rare cases where you may want to do a full refresh on a
    /// partial region. 99.9% of of the time, you want this set to `false`.
    ///
    /// Some additional points to note:
    ///
    ///    1) PxP must process 8x8 pixel blocks, and all pixels in each block
    ///    are considered for auto-waveform mode selection. If the
    ///    update region is not 8x8 aligned, additional unwanted pixels
    ///    will be considered in auto-waveform mode selection.
    ///
    ///    2) PxP input must be 32-bit aligned, so any update
    ///    address not 32-bit aligned must be shifted to meet the
    ///    32-bit alignment.  The PxP will thus end up processing pixels
    ///    outside of the update region to satisfy this alignment restriction,
    ///    which can affect auto-waveform mode selection.
    ///
    ///    3) If input fails 32-bit alignment, and the resulting expansion
    ///    of the processed region would add at least 8 pixels more per
    ///    line than the original update line width, the EPDC would
    ///    cause screen artifacts by incorrectly handling the 8+ pixels
    ///    at the end of each line.
    #[allow(clippy::too_many_arguments)]
    fn partial_refresh(
        &self,
        region: &common::mxcfb_rect,
        mode: PartialRefreshMode,
        waveform_mode: common::waveform_mode,
        temperature: common::display_temp,
        dither_mode: common::dither_mode,
        quant_bit: i32,
        force_full_refresh: bool,
    ) -> u32;

    /// Takes a marker returned by `partial_refresh` and blocks until that
    /// refresh has been reflected on the display.
    /// Returns the collusion_test result which is supposed to be
    /// related to the collusion information.
    fn wait_refresh_complete(&self, marker: u32) -> u32;
}