1use super::linuxfb_sys::*;
2use super::mxcfb_sys::*;
3use super::transform::*;
4use super::{Framebuffer, UpdateMode};
5use crate::color::Color;
6use crate::device::{Model, CURRENT_DEVICE};
7use crate::geom::Rectangle;
8use anyhow::{Context, Error};
9use std::fs::{File, OpenOptions};
10use std::io::{self, Write};
11use std::ops::Drop;
12use std::os::unix::io::AsRawFd;
13use std::path::Path;
14use std::ptr;
15use std::slice;
16use tracing::{error, info};
17
18impl Into<MxcfbRect> for Rectangle {
19 fn into(self) -> MxcfbRect {
20 MxcfbRect {
21 top: self.min.y as u32,
22 left: self.min.x as u32,
23 width: self.width(),
24 height: self.height(),
25 }
26 }
27}
28
29type SetPixelRgb = fn(&mut KoboFramebuffer1, u32, u32, [u8; 3]);
30type GetPixelRgb = fn(&KoboFramebuffer1, u32, u32) -> [u8; 3];
31type AsRgb = fn(&KoboFramebuffer1) -> Vec<u8>;
32
33pub struct KoboFramebuffer1 {
34 file: File,
35 frame: *mut libc::c_void,
36 frame_size: libc::size_t,
37 token: u32,
38 flags: u32,
39 monochrome: bool,
40 dithered: bool,
41 inverted: bool,
42 transform: ColorTransform,
43 set_pixel_rgb: SetPixelRgb,
44 get_pixel_rgb: GetPixelRgb,
45 as_rgb: AsRgb,
46 red_index: usize,
47 green_index: usize,
48 blue_index: usize,
49 bytes_per_pixel: u8,
50 var_info: VarScreenInfo,
51 fix_info: FixScreenInfo,
52}
53
54impl KoboFramebuffer1 {
55 pub fn new<P: AsRef<Path>>(path: P) -> Result<KoboFramebuffer1, Error> {
56 let file = OpenOptions::new()
57 .read(true)
58 .write(true)
59 .open(&path)
60 .with_context(|| {
61 format!("can't open framebuffer device {}", path.as_ref().display())
62 })?;
63
64 let var_info = var_screen_info(&file)?;
65 let fix_info = fix_screen_info(&file)?;
66
67 assert_eq!(var_info.bits_per_pixel % 8, 0);
68
69 let bytes_per_pixel = var_info.bits_per_pixel / 8;
70 let frame_size = (var_info.yres * fix_info.line_length) as libc::size_t;
71
72 let frame = unsafe {
73 libc::mmap(
74 ptr::null_mut(),
75 fix_info.smem_len as usize,
76 libc::PROT_READ | libc::PROT_WRITE,
77 libc::MAP_SHARED,
78 file.as_raw_fd(),
79 0,
80 )
81 };
82
83 if frame == libc::MAP_FAILED {
84 Err(Error::from(io::Error::last_os_error()).context("can't map memory"))
85 } else {
86 let (set_pixel_rgb, get_pixel_rgb, as_rgb): (SetPixelRgb, GetPixelRgb, AsRgb) =
87 if var_info.bits_per_pixel > 16 {
88 (set_pixel_rgb_32, get_pixel_rgb_32, as_rgb_32)
89 } else if var_info.bits_per_pixel > 8 {
90 (set_pixel_rgb_16, get_pixel_rgb_16, as_rgb_16)
91 } else {
92 (set_pixel_rgb_8, get_pixel_rgb_8, as_rgb_8)
93 };
94 let red_index = if var_info.red.offset > 0 { 2 } else { 0 };
95 Ok(KoboFramebuffer1 {
96 file,
97 frame,
98 frame_size,
99 token: 1,
100 flags: 0,
101 monochrome: false,
102 dithered: false,
103 inverted: false,
104 transform: transform_identity,
105 set_pixel_rgb,
106 get_pixel_rgb,
107 as_rgb,
108 red_index,
109 green_index: 1,
110 blue_index: 2 - red_index,
111 bytes_per_pixel: bytes_per_pixel as u8,
112 var_info,
113 fix_info,
114 })
115 }
116 }
117
118 fn as_bytes(&self) -> &[u8] {
119 unsafe { slice::from_raw_parts(self.frame as *const u8, self.frame_size) }
120 }
121}
122
123impl Framebuffer for KoboFramebuffer1 {
124 fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
125 let c = (self.transform)(x, y, color);
126 (self.set_pixel_rgb)(self, x, y, c.rgb());
127 }
128
129 fn set_blended_pixel(&mut self, x: u32, y: u32, color: Color, alpha: f32) {
130 if alpha >= 1.0 {
131 self.set_pixel(x, y, color);
132 return;
133 }
134 let background = Color::from_rgb(&(self.get_pixel_rgb)(self, x, y));
135 let interp = background.lerp(color, alpha);
136 let c = (self.transform)(x, y, interp);
137 (self.set_pixel_rgb)(self, x, y, c.rgb());
138 }
139
140 fn invert_region(&mut self, rect: &Rectangle) {
141 for y in rect.min.y..rect.max.y {
142 for x in rect.min.x..rect.max.x {
143 let rgb = (self.get_pixel_rgb)(self, x as u32, y as u32);
144 let color = [255 - rgb[0], 255 - rgb[1], 255 - rgb[2]];
145 (self.set_pixel_rgb)(self, x as u32, y as u32, color);
146 }
147 }
148 }
149
150 fn shift_region(&mut self, rect: &Rectangle, drift: u8) {
151 for y in rect.min.y..rect.max.y {
152 for x in rect.min.x..rect.max.x {
153 let rgb = (self.get_pixel_rgb)(self, x as u32, y as u32);
154 let color = [
155 rgb[0].saturating_sub(drift),
156 rgb[1].saturating_sub(drift),
157 rgb[2].saturating_sub(drift),
158 ];
159 (self.set_pixel_rgb)(self, x as u32, y as u32, color);
160 }
161 }
162 }
163
164 fn update(&mut self, rect: &Rectangle, mode: UpdateMode) -> Result<u32, Error> {
166 let update_marker = self.token;
167 let mark = CURRENT_DEVICE.mark();
168 let color_samples = CURRENT_DEVICE.color_samples();
169 let mut flags = self.flags;
170 let mut monochrome = self.monochrome;
171 let mut dithered = self.dithered;
172
173 let (update_mode, mut waveform_mode) = match mode {
174 UpdateMode::Gui => (UPDATE_MODE_PARTIAL, WAVEFORM_MODE_AUTO),
175 UpdateMode::Partial => {
176 if mark >= 12 && color_samples > 1 && dithered {
177 (UPDATE_MODE_FULL, HWTCON_WAVEFORM_MODE_GLRC16)
178 } else if mark >= 11 {
179 (UPDATE_MODE_PARTIAL, HWTCON_WAVEFORM_MODE_GLR16)
180 } else if mark >= 7 {
181 (UPDATE_MODE_PARTIAL, NTX_WFM_MODE_GLR16)
182 } else if CURRENT_DEVICE.model == Model::Aura {
183 flags |= EPDC_FLAG_USE_AAD;
184 (UPDATE_MODE_FULL, NTX_WFM_MODE_GLD16)
185 } else {
186 (UPDATE_MODE_PARTIAL, WAVEFORM_MODE_AUTO)
187 }
188 }
189 UpdateMode::Full => {
190 monochrome = false;
191 if mark >= 12 && color_samples > 1 && dithered {
192 (UPDATE_MODE_FULL, HWTCON_WAVEFORM_MODE_GCC16)
193 } else {
194 (UPDATE_MODE_FULL, NTX_WFM_MODE_GC16)
195 }
196 }
197 UpdateMode::Fast => {
198 if mark >= 11 {
199 (UPDATE_MODE_PARTIAL, HWTCON_WAVEFORM_MODE_A2)
200 } else {
201 (UPDATE_MODE_PARTIAL, NTX_WFM_MODE_A2)
202 }
203 }
204 UpdateMode::FastMono => {
205 if mark >= 11 {
206 flags |= HWTCON_FLAG_FORCE_A2_OUTPUT;
207 (UPDATE_MODE_PARTIAL, HWTCON_WAVEFORM_MODE_A2)
208 } else {
209 flags |= EPDC_FLAG_FORCE_MONOCHROME;
210 (UPDATE_MODE_PARTIAL, NTX_WFM_MODE_A2)
211 }
212 }
213 };
214
215 if monochrome {
216 if mark >= 11 {
217 if waveform_mode != HWTCON_WAVEFORM_MODE_A2 {
218 waveform_mode = NTX_WFM_MODE_DU;
219 dithered = true;
220 }
221 } else if mark >= 7 {
222 if waveform_mode != NTX_WFM_MODE_A2 {
223 waveform_mode = NTX_WFM_MODE_DU;
224 dithered = true;
225 }
226 } else {
227 waveform_mode = NTX_WFM_MODE_A2;
228 }
229 }
230
231 if self.inverted {
232 if mark >= 11 {
233 if waveform_mode == HWTCON_WAVEFORM_MODE_GLR16 {
234 waveform_mode = HWTCON_WAVEFORM_MODE_GLKW16;
235 } else if waveform_mode == NTX_WFM_MODE_GC16 {
236 waveform_mode = HWTCON_WAVEFORM_MODE_GCK16;
237 }
238 } else if mark >= 9 {
239 if waveform_mode == NTX_WFM_MODE_GLR16 {
240 waveform_mode = NTX_WFM_MODE_GLKW16;
241 } else if waveform_mode == NTX_WFM_MODE_GC16 {
242 waveform_mode = NTX_WFM_MODE_GCK16;
243 }
244 }
245 }
246
247 let result = if mark >= 11 {
248 let mut dither_mode = 0;
249
250 if dithered {
251 flags |= HWTCON_FLAG_USE_DITHERING;
252 if monochrome {
253 dither_mode = HWTCON_FLAG_USE_DITHERING_Y8_Y1_S
254 } else {
255 dither_mode = HWTCON_FLAG_USE_DITHERING_Y8_Y4_S;
256 }
257 }
258
259 let update_data = HwtConUpdateData {
260 update_region: (*rect).into(),
261 waveform_mode,
262 update_mode,
263 update_marker,
264 flags,
265 dither_mode,
266 };
267 unsafe { send_update_v3(self.file.as_raw_fd(), &update_data) }
268 } else if mark >= 7 {
269 let mut quant_bit = 0;
270 let mut dither_mode = EPDC_FLAG_USE_DITHERING_PASSTHROUGH;
271
272 if dithered {
273 if monochrome {
274 flags |= EPDC_FLAG_USE_DITHERING_Y1;
275 } else if mode == UpdateMode::Partial || mode == UpdateMode::Full {
276 dither_mode = EPDC_FLAG_USE_DITHERING_ORDERED;
277 quant_bit = 7;
278 }
279 }
280
281 let update_data = MxcfbUpdateDataV2 {
282 update_region: (*rect).into(),
283 waveform_mode,
284 update_mode,
285 update_marker,
286 temp: TEMP_USE_AMBIENT,
287 flags,
288 dither_mode,
289 quant_bit,
290 alt_buffer_data: MxcfbAltBufferDataV2::default(),
291 };
292 unsafe { send_update_v2(self.file.as_raw_fd(), &update_data) }
293 } else {
294 if monochrome && !dithered {
295 flags |= EPDC_FLAG_FORCE_MONOCHROME;
296 }
297
298 let update_data = MxcfbUpdateDataV1 {
299 update_region: (*rect).into(),
300 waveform_mode,
301 update_mode,
302 update_marker,
303 temp: TEMP_USE_AMBIENT,
304 flags,
305 alt_buffer_data: MxcfbAltBufferDataV1::default(),
306 };
307 unsafe { send_update_v1(self.file.as_raw_fd(), &update_data) }
308 };
309
310 match result {
311 Err(e) => Err(Error::from(e).context("can't send framebuffer update")),
312 _ => {
313 self.token = self.token.wrapping_add(1);
314 Ok(update_marker)
315 }
316 }
317 }
318
319 fn wait(&self, token: u32) -> Result<i32, Error> {
321 let result = if CURRENT_DEVICE.mark() >= 7 {
322 let mut marker_data = MxcfbUpdateMarkerData {
323 update_marker: token,
324 collision_test: 0,
325 };
326 unsafe { wait_for_update_v2(self.file.as_raw_fd(), &mut marker_data) }
327 } else {
328 unsafe { wait_for_update_v1(self.file.as_raw_fd(), &token) }
329 };
330 result.context("can't wait for framebuffer update")
331 }
332
333 fn save(&self, path: &str) -> Result<(), Error> {
334 let (width, height) = self.dims();
335 let file =
336 File::create(path).with_context(|| format!("can't create output file {}", path))?;
337 let mut encoder = png::Encoder::new(file, width, height);
338 encoder.set_depth(png::BitDepth::Eight);
339 encoder.set_color(png::ColorType::Rgb);
340 let mut writer = encoder
341 .write_header()
342 .with_context(|| format!("can't write PNG header for {}", path))?;
343 writer
344 .write_image_data(&(self.as_rgb)(self))
345 .with_context(|| format!("can't write PNG data to {}", path))?;
346 Ok(())
347 }
348
349 #[inline]
350 fn rotation(&self) -> i8 {
351 self.var_info.rotate as i8
352 }
353
354 fn set_rotation(&mut self, n: i8) -> Result<(u32, u32), Error> {
355 let read_rotation = self.rotation();
356
357 for (i, v) in [n, (n + 1) % 4, n].iter().enumerate() {
362 self.var_info.rotate = *v as u32;
363
364 let result =
365 unsafe { write_variable_screen_info(self.file.as_raw_fd(), &self.var_info) };
366
367 if let Err(e) = result {
368 return Err(Error::from(e).context("can't set variable screen info"));
369 }
370
371 if i == 0 && read_rotation != self.rotation() {
373 break;
374 }
375 }
376
377 self.fix_info = fix_screen_info(&self.file)?;
378 self.frame_size = (self.var_info.yres * self.fix_info.line_length) as libc::size_t;
379
380 info!("Framebuffer rotation: {} -> {}.", n, self.rotation());
381
382 Ok((self.var_info.xres, self.var_info.yres))
383 }
384
385 fn set_inverted(&mut self, enable: bool) {
386 if self.inverted == enable {
387 return;
388 }
389 self.inverted = enable;
390 if CURRENT_DEVICE.mark() < 11 {
391 if enable {
392 self.flags |= EPDC_FLAG_ENABLE_INVERSION;
393 } else {
394 self.flags &= !EPDC_FLAG_ENABLE_INVERSION;
395 }
396 } else {
397 OpenOptions::new()
398 .read(false)
399 .write(true)
400 .open("/proc/hwtcon/cmd")
401 .and_then(|mut file| {
402 file.write_all(if enable {
403 b"night_mode 4"
404 } else {
405 b"night_mode 0"
406 })
407 })
408 .map_err(|e| error!("Failed to invert colors: {:#?}", e))
409 .ok();
410 }
411 }
412
413 fn inverted(&self) -> bool {
414 self.inverted
415 }
416
417 fn set_monochrome(&mut self, enable: bool) {
418 self.monochrome = enable;
419 }
420
421 fn monochrome(&self) -> bool {
422 self.monochrome
423 }
424
425 fn set_dithered(&mut self, enable: bool) {
426 if enable == self.dithered {
427 return;
428 }
429
430 self.dithered = enable;
431
432 if CURRENT_DEVICE.mark() < 7 {
433 if enable {
434 self.transform = transform_dither_g16;
435 } else {
436 self.transform = transform_identity;
437 }
438 }
439 }
440
441 fn dithered(&self) -> bool {
442 self.dithered
443 }
444
445 fn width(&self) -> u32 {
446 self.var_info.xres
447 }
448
449 fn height(&self) -> u32 {
450 self.var_info.yres
451 }
452}
453
454impl Drop for KoboFramebuffer1 {
455 fn drop(&mut self) {
456 unsafe {
457 libc::munmap(self.frame, self.fix_info.smem_len as usize);
458 }
459 }
460}
461
462fn set_pixel_rgb_8(fb: &mut KoboFramebuffer1, x: u32, y: u32, rgb: [u8; 3]) {
463 let addr = (fb.var_info.xoffset as isize + x as isize) * (fb.bytes_per_pixel as isize)
464 + (fb.var_info.yoffset as isize + y as isize) * (fb.fix_info.line_length as isize);
465
466 debug_assert!(addr < fb.frame_size as isize);
467
468 unsafe {
469 let spot = fb.frame.offset(addr) as *mut u8;
470 *spot = rgb[0];
471 }
472}
473
474fn set_pixel_rgb_16(fb: &mut KoboFramebuffer1, x: u32, y: u32, rgb: [u8; 3]) {
475 let addr = (fb.var_info.xoffset as isize + x as isize) * (fb.bytes_per_pixel as isize)
476 + (fb.var_info.yoffset as isize + y as isize) * (fb.fix_info.line_length as isize);
477
478 debug_assert!(addr < fb.frame_size as isize);
479
480 unsafe {
481 let spot = fb.frame.offset(addr) as *mut u8;
482 *spot.offset(0) = rgb[2] >> 3 | (rgb[1] & 0b0001_1100) << 3;
483 *spot.offset(1) = (rgb[0] & 0b1111_1000) | rgb[1] >> 5;
484 }
485}
486
487fn set_pixel_rgb_32(fb: &mut KoboFramebuffer1, x: u32, y: u32, rgb: [u8; 3]) {
488 let addr = (fb.var_info.xoffset as isize + x as isize) * (fb.bytes_per_pixel as isize)
489 + (fb.var_info.yoffset as isize + y as isize) * (fb.fix_info.line_length as isize);
490
491 debug_assert!(addr < fb.frame_size as isize);
492
493 unsafe {
494 let spot = fb.frame.offset(addr) as *mut u8;
495 *spot.offset(0) = rgb[fb.red_index];
496 *spot.offset(1) = rgb[fb.green_index];
497 *spot.offset(2) = rgb[fb.blue_index];
498 }
500}
501
502fn get_pixel_rgb_8(fb: &KoboFramebuffer1, x: u32, y: u32) -> [u8; 3] {
503 let addr = (fb.var_info.xoffset as isize + x as isize) * (fb.bytes_per_pixel as isize)
504 + (fb.var_info.yoffset as isize + y as isize) * (fb.fix_info.line_length as isize);
505 let gray = unsafe { *(fb.frame.offset(addr) as *const u8) };
506 [gray, gray, gray]
507}
508
509fn get_pixel_rgb_16(fb: &KoboFramebuffer1, x: u32, y: u32) -> [u8; 3] {
510 let addr = (fb.var_info.xoffset as isize + x as isize) * (fb.bytes_per_pixel as isize)
511 + (fb.var_info.yoffset as isize + y as isize) * (fb.fix_info.line_length as isize);
512 let pair = unsafe {
513 let spot = fb.frame.offset(addr) as *mut u8;
514 [*spot.offset(0), *spot.offset(1)]
515 };
516 let red = pair[1] & 0b1111_1000;
517 let green = ((pair[1] & 0b0000_0111) << 5) | ((pair[0] & 0b1110_0000) >> 3);
518 let blue = (pair[0] & 0b0001_1111) << 3;
519 [red, green, blue]
520}
521
522fn get_pixel_rgb_32(fb: &KoboFramebuffer1, x: u32, y: u32) -> [u8; 3] {
523 let addr = (fb.var_info.xoffset as isize + x as isize) * (fb.bytes_per_pixel as isize)
524 + (fb.var_info.yoffset as isize + y as isize) * (fb.fix_info.line_length as isize);
525 unsafe {
526 let spot = fb.frame.offset(addr) as *mut u8;
527 [
528 *spot.offset(fb.red_index as isize),
529 *spot.offset(fb.green_index as isize),
530 *spot.offset(fb.blue_index as isize),
531 ]
532 }
533}
534
535fn as_rgb_8(fb: &KoboFramebuffer1) -> Vec<u8> {
536 let (width, height) = fb.dims();
537 let mut rgb888 = Vec::with_capacity((width * height * 3) as usize);
538 let rgb8 = fb.as_bytes();
539 let virtual_width = fb.var_info.xres_virtual as usize;
540 for (_, &gray) in rgb8
541 .iter()
542 .take(height as usize * virtual_width)
543 .enumerate()
544 .filter(|&(i, _)| i % virtual_width < width as usize)
545 {
546 rgb888.extend_from_slice(&[gray, gray, gray]);
547 }
548 rgb888
549}
550
551fn as_rgb_16(fb: &KoboFramebuffer1) -> Vec<u8> {
552 let (width, height) = fb.dims();
553 let mut rgb888 = Vec::with_capacity((width * height * 3) as usize);
554 let rgb565 = fb.as_bytes();
555 let virtual_width = fb.var_info.xres_virtual as usize;
556 for (_, pair) in rgb565
557 .chunks(2)
558 .take(height as usize * virtual_width)
559 .enumerate()
560 .filter(|&(i, _)| i % virtual_width < width as usize)
561 {
562 let red = pair[1] & 0b1111_1000;
563 let green = ((pair[1] & 0b0000_0111) << 5) | ((pair[0] & 0b1110_0000) >> 3);
564 let blue = (pair[0] & 0b0001_1111) << 3;
565 rgb888.extend_from_slice(&[red, green, blue]);
566 }
567 rgb888
568}
569
570fn as_rgb_32(fb: &KoboFramebuffer1) -> Vec<u8> {
571 let (width, height) = fb.dims();
572 let mut rgb888 = Vec::with_capacity((width * height * 3) as usize);
573 let data = fb.as_bytes();
574 let virtual_width = fb.var_info.xres_virtual as usize;
575 for (_, color) in data
576 .chunks(4)
577 .take(height as usize * virtual_width)
578 .enumerate()
579 .filter(|&(i, _)| i % virtual_width < width as usize)
580 {
581 let red = color[fb.red_index];
582 let green = color[fb.green_index];
583 let blue = color[fb.blue_index];
584 rgb888.extend_from_slice(&[red, green, blue]);
585 }
586 rgb888
587}