1 use alloc::{sync::Arc, vec::Vec}; 2 use system_error::SystemError; 3 4 use crate::{ 5 driver::{ 6 tty::{ 7 console::ConsoleSwitch, 8 virtual_terminal::{ 9 virtual_console::{CursorOperation, ScrollDir, VcCursor, VirtualConsoleData}, 10 Color, 11 }, 12 }, 13 video::fbdev::base::{ 14 CopyAreaData, FbCursor, FbCursorSetMode, FbImage, FbVisual, FillRectData, FillRectROP, 15 FrameBuffer, ScrollMode, FRAME_BUFFER_SET, 16 }, 17 }, 18 libs::{ 19 font::FontDesc, 20 spinlock::{SpinLock, SpinLockGuard}, 21 }, 22 }; 23 24 use super::{FbConAttr, FrameBufferConsole, FrameBufferConsoleData}; 25 26 #[derive(Debug)] 27 pub struct BlittingFbConsole { 28 fb: SpinLock<Option<Arc<dyn FrameBuffer>>>, 29 fbcon_data: SpinLock<FrameBufferConsoleData>, 30 } 31 32 unsafe impl Send for BlittingFbConsole {} 33 unsafe impl Sync for BlittingFbConsole {} 34 35 impl BlittingFbConsole { 36 pub fn new() -> Result<Self, SystemError> { 37 Ok(Self { 38 fb: SpinLock::new(None), 39 fbcon_data: SpinLock::new(FrameBufferConsoleData::default()), 40 }) 41 } 42 43 pub fn fb(&self) -> Arc<dyn FrameBuffer> { 44 self.fb.lock().clone().unwrap() 45 } 46 47 pub fn get_color(&self, vc_data: &VirtualConsoleData, c: u16, is_fg: bool) -> u32 { 48 let fb_info = self.fb(); 49 let mut color = 0; 50 51 let depth = fb_info.color_depth(); 52 53 if depth != 1 { 54 if is_fg { 55 let fg_shift = if vc_data.hi_font_mask != 0 { 9 } else { 8 }; 56 color = (c as u32 >> fg_shift) & 0x0f 57 } else { 58 let bg_shift = if vc_data.hi_font_mask != 0 { 13 } else { 12 }; 59 color = (c as u32 >> bg_shift) & 0x0f 60 } 61 } 62 63 match depth { 64 1 => { 65 let col = self.mono_color(); 66 let fg; 67 let bg; 68 if fb_info.current_fb_fix().visual != FbVisual::Mono01 { 69 fg = col; 70 bg = 0; 71 } else { 72 fg = 0; 73 bg = col; 74 } 75 color = if is_fg { fg } else { bg }; 76 } 77 2 => { 78 /* 79 颜色深度为2,即16色, 80 将16色的颜色值映射到4色的灰度, 81 其中颜色0映射为黑色,颜色1到6映射为白色, 82 颜色7到8映射为灰色,其他颜色映射为强烈的白色。 83 */ 84 if color >= 1 && color <= 6 { 85 // 白色 86 color = 2; 87 } else if color >= 7 && color <= 8 { 88 // 灰色 89 color = 1; 90 } else { 91 // 强白 92 color = 3; 93 } 94 } 95 3 => { 96 /* 97 颜色深度为3,即256色,仅保留颜色的低3位,即颜色 0 到 7 98 */ 99 color &= 7; 100 } 101 _ => {} 102 } 103 color 104 } 105 106 /// ## 计算单色调的函数 107 pub fn mono_color(&self) -> u32 { 108 let fb_info = self.fb(); 109 let mut max_len = fb_info 110 .current_fb_var() 111 .green 112 .length 113 .max(fb_info.current_fb_var().red.length); 114 115 max_len = max_len.max(fb_info.current_fb_var().blue.length); 116 117 return (!(0xfff << max_len)) & 0xff; 118 } 119 120 pub fn bit_put_string( 121 &self, 122 vc_data: &VirtualConsoleData, 123 buf: &[u16], 124 attr: FbConAttr, 125 cnt: u32, 126 cellsize: u32, 127 image: &mut FbImage, 128 ) { 129 let charmask = if vc_data.hi_font_mask != 0 { 130 0x1ff 131 } else { 132 0xff 133 }; 134 135 let mut offset; 136 let image_line_byte = image.width as usize / 8; 137 138 let byte_width = vc_data.font.width as usize / 8; 139 let font_height = vc_data.font.height as usize; 140 // let mut char_offset = 0; 141 for char_offset in 0..cnt as usize { 142 // 在字符表中的index 143 let ch = buf[char_offset] & charmask; 144 // 计算出在font表中的偏移量 145 let font_offset = ch as usize * cellsize as usize; 146 let font_offset_end = font_offset + cellsize as usize; 147 // 设置image的data 148 149 let src = &vc_data.font.data[font_offset..font_offset_end]; 150 let mut dst = Vec::new(); 151 dst.resize(src.len(), 0); 152 dst.copy_from_slice(src); 153 154 if !attr.is_empty() { 155 attr.update_attr(&mut dst, src, vc_data) 156 } 157 158 offset = char_offset * byte_width; 159 let mut dst_offset = 0; 160 for _ in 0..font_height { 161 let dst_offset_next = dst_offset + byte_width; 162 image.data[offset..offset + byte_width] 163 .copy_from_slice(&dst[dst_offset..dst_offset_next]); 164 165 offset += image_line_byte; 166 dst_offset = dst_offset_next; 167 } 168 } 169 170 self.fb().fb_image_blit(image); 171 } 172 } 173 174 impl ConsoleSwitch for BlittingFbConsole { 175 fn con_init( 176 &self, 177 vc_data: &mut VirtualConsoleData, 178 init: bool, 179 ) -> Result<(), system_error::SystemError> { 180 let fb_set_guard = FRAME_BUFFER_SET.read(); 181 let fb = fb_set_guard.get(vc_data.index); 182 if fb.is_none() { 183 return Err(SystemError::EINVAL); 184 } 185 let fb = fb.unwrap(); 186 if fb.is_none() { 187 panic!( 188 "The Framebuffer with FbID {} has not been initialized yet.", 189 vc_data.index 190 ) 191 } 192 193 let fb = fb.as_ref().unwrap().clone(); 194 195 if init { 196 // 初始化字体 197 let var = fb.current_fb_var(); 198 let font = FontDesc::get_default_font(var.xres, var.yres, 0, 0); 199 vc_data.font.data = font.data.to_vec(); 200 vc_data.font.width = font.width; 201 vc_data.font.height = font.height; 202 vc_data.font.count = font.char_count; 203 } else { 204 kwarn!("The frontend Framebuffer is not implemented"); 205 } 206 207 vc_data.color_mode = fb.color_depth() != 1; 208 vc_data.complement_mask = if vc_data.color_mode { 0x7700 } else { 0x0800 }; 209 210 if vc_data.font.count == 256 { 211 // ascii 212 vc_data.hi_font_mask = 0; 213 } else { 214 vc_data.hi_font_mask = 0x100; 215 if vc_data.color_mode { 216 vc_data.complement_mask <<= 1; 217 } 218 } 219 220 // TODO: 考虑rotate 221 if init { 222 vc_data.cols = (fb.current_fb_var().xres / vc_data.font.width) as usize; 223 vc_data.rows = (fb.current_fb_var().yres / vc_data.font.height) as usize; 224 225 vc_data.pos = vc_data.state.x + vc_data.state.y * vc_data.cols; 226 227 let new_size = vc_data.cols * vc_data.rows; 228 vc_data.screen_buf.resize(new_size, 0); 229 } else { 230 unimplemented!("Resize is not supported at the moment!"); 231 } 232 233 // 初始化fb 234 *self.fb.lock() = Some(fb); 235 236 Ok(()) 237 } 238 239 fn con_deinit(&self) -> Result<(), system_error::SystemError> { 240 todo!() 241 } 242 243 fn con_clear( 244 &self, 245 vc_data: &mut VirtualConsoleData, 246 sy: usize, 247 sx: usize, 248 height: usize, 249 width: usize, 250 ) -> Result<(), system_error::SystemError> { 251 let fb_data = self.fbcon_data(); 252 253 if height == 0 || width == 0 { 254 return Ok(()); 255 } 256 257 let y_break = (fb_data.display.virt_rows - fb_data.display.yscroll) as usize; 258 if sy < y_break && sy + height - 1 >= y_break { 259 // 分两次clear 260 let b = y_break - sy; 261 let _ = self.clear( 262 &vc_data, 263 fb_data.display.real_y(sy as u32), 264 sx as u32, 265 b as u32, 266 width as u32, 267 ); 268 let _ = self.clear( 269 &vc_data, 270 fb_data.display.real_y((sy + b) as u32), 271 sx as u32, 272 (height - b) as u32, 273 width as u32, 274 ); 275 } else { 276 let _ = self.clear( 277 &vc_data, 278 fb_data.display.real_y(sy as u32), 279 sx as u32, 280 height as u32, 281 width as u32, 282 ); 283 } 284 285 Ok(()) 286 } 287 288 fn con_putc( 289 &self, 290 vc_data: &VirtualConsoleData, 291 ch: u16, 292 xpos: u32, 293 ypos: u32, 294 ) -> Result<(), system_error::SystemError> { 295 self.con_putcs(vc_data, &[ch], 1, ypos, xpos) 296 } 297 298 fn con_putcs( 299 &self, 300 vc_data: &VirtualConsoleData, 301 buf: &[u16], 302 count: usize, 303 ypos: u32, 304 xpos: u32, 305 ) -> Result<(), SystemError> { 306 if count == 0 { 307 return Ok(()); 308 } 309 let fbcon_data = self.fbcon_data(); 310 let c = buf[0]; 311 self.put_string( 312 vc_data, 313 buf, 314 count as u32, 315 fbcon_data.display.real_y(ypos), 316 xpos, 317 self.get_color(vc_data, c, true), 318 self.get_color(vc_data, c, false), 319 ) 320 } 321 322 fn con_getxy( 323 &self, 324 vc_data: &VirtualConsoleData, 325 pos: usize, 326 ) -> Result<(usize, usize, usize), SystemError> { 327 if pos < vc_data.screen_buf.len() { 328 let x = pos % vc_data.cols; 329 let y = pos / vc_data.cols; 330 let mut next_line_start = pos + (vc_data.cols - x); 331 if next_line_start >= vc_data.screen_buf.len() { 332 next_line_start = 0 333 } 334 return Ok((next_line_start, x, y)); 335 } else { 336 return Ok((0, 0, 0)); 337 } 338 } 339 340 fn con_cursor( 341 &self, 342 vc_data: &VirtualConsoleData, 343 op: crate::driver::tty::virtual_terminal::virtual_console::CursorOperation, 344 ) { 345 let mut fbcon_data = self.fbcon_data(); 346 347 let c = vc_data.screen_buf[vc_data.pos]; 348 349 if vc_data.cursor_type.contains(VcCursor::CUR_SW) { 350 // 取消硬光标Timer,但是现在没有硬光标,先写在这 351 } else { 352 // 添加硬光标Timer 353 } 354 355 fbcon_data.cursor_flash = op != CursorOperation::Erase; 356 357 drop(fbcon_data); 358 359 self.cursor( 360 vc_data, 361 op, 362 self.get_color(vc_data, c, true), 363 self.get_color(vc_data, c, false), 364 ); 365 } 366 367 fn con_set_palette( 368 &self, 369 vc_data: &VirtualConsoleData, 370 color_table: &[u8], 371 ) -> Result<(), SystemError> { 372 let fb_info = self.fb(); 373 let depth = fb_info.color_depth(); 374 let mut palette = Vec::new(); 375 palette.resize(16, Color::default()); 376 if depth > 3 { 377 let vc_palette = &vc_data.palette; 378 for i in 0..16 { 379 let idx = color_table[i]; 380 let col = palette.get_mut(idx as usize).unwrap(); 381 col.red = (vc_palette[i].red << 8) | vc_palette[i].red; 382 col.green = (vc_palette[i].green << 8) | vc_palette[i].green; 383 col.blue = (vc_palette[i].blue << 8) | vc_palette[i].blue; 384 } 385 } else { 386 todo!() 387 } 388 389 self.fb().set_color_map(palette)?; 390 391 Ok(()) 392 } 393 394 fn con_scroll( 395 &self, 396 vc_data: &mut VirtualConsoleData, 397 top: usize, 398 bottom: usize, 399 dir: crate::driver::tty::virtual_terminal::virtual_console::ScrollDir, 400 mut count: usize, 401 ) -> bool { 402 self.con_cursor(vc_data, CursorOperation::Erase); 403 404 let fbcon_data = self.fbcon_data(); 405 let scroll_mode = fbcon_data.display.scroll_mode; 406 407 drop(fbcon_data); 408 409 match dir { 410 ScrollDir::Up => { 411 if count > vc_data.rows { 412 count = vc_data.rows; 413 } 414 415 match scroll_mode { 416 ScrollMode::Move => { 417 let start = top * vc_data.cols; 418 let end = bottom * vc_data.cols; 419 vc_data.screen_buf[start..end].rotate_left(count * vc_data.cols); 420 421 let _ = self.bmove( 422 vc_data, 423 top as i32, 424 0, 425 top as i32 - count as i32, 426 0, 427 (bottom - top) as u32, 428 vc_data.cols as u32, 429 ); 430 431 let _ = self.con_clear(vc_data, bottom - count, 0, count, vc_data.cols); 432 433 let offset = vc_data.cols * (bottom - count); 434 for i in 435 vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut() 436 { 437 *i = vc_data.erase_char; 438 } 439 440 return true; 441 } 442 ScrollMode::PanMove => todo!(), 443 ScrollMode::WrapMove => todo!(), 444 ScrollMode::Redraw => { 445 let start = top * vc_data.cols; 446 let end = bottom * vc_data.cols; 447 vc_data.screen_buf[start..end].rotate_left(count * vc_data.cols); 448 449 let data = &vc_data.screen_buf[start..(bottom - count) * vc_data.cols]; 450 451 for line in top..(bottom - count) { 452 let mut start = line * vc_data.cols; 453 let end = start + vc_data.cols; 454 let mut offset = start; 455 let mut attr = 1; 456 let mut x = 0; 457 while offset < end { 458 let c = data[offset]; 459 460 if attr != c & 0xff00 { 461 // 属性变化,输出完上一个的并且更新属性 462 attr = c & 0xff00; 463 464 let count = offset - start; 465 let _ = self.con_putcs( 466 vc_data, 467 &data[start..offset], 468 count, 469 line as u32, 470 x, 471 ); 472 start = offset; 473 x += count as u32; 474 } 475 476 offset += 1; 477 } 478 let _ = self.con_putcs( 479 vc_data, 480 &data[start..offset], 481 offset - start, 482 line as u32, 483 x, 484 ); 485 } 486 487 let _ = self.con_clear(vc_data, bottom - count, 0, count, vc_data.cols); 488 489 let offset = vc_data.cols * (bottom - count); 490 for i in 491 vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut() 492 { 493 *i = vc_data.erase_char; 494 } 495 496 return true; 497 } 498 ScrollMode::PanRedraw => todo!(), 499 } 500 } 501 ScrollDir::Down => { 502 if count > vc_data.rows { 503 count = vc_data.rows; 504 } 505 506 match scroll_mode { 507 ScrollMode::Move => todo!(), 508 ScrollMode::PanMove => todo!(), 509 ScrollMode::WrapMove => todo!(), 510 ScrollMode::Redraw => { 511 // self.scroll_redraw( 512 // vc_data, 513 // bottom - 1, 514 // bottom - top - count, 515 // count * vc_data.cols, 516 // false, 517 // ); 518 519 let _ = self.con_clear(vc_data, top, 0, count, vc_data.cols); 520 521 let offset = vc_data.cols * top; 522 for i in 523 vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut() 524 { 525 *i = vc_data.erase_char; 526 } 527 528 return true; 529 } 530 ScrollMode::PanRedraw => todo!(), 531 } 532 } 533 } 534 } 535 } 536 537 impl FrameBufferConsole for BlittingFbConsole { 538 fn bmove( 539 &self, 540 vc_data: &VirtualConsoleData, 541 sy: i32, 542 sx: i32, 543 dy: i32, 544 dx: i32, 545 height: u32, 546 width: u32, 547 ) -> Result<(), SystemError> { 548 let area = CopyAreaData::new( 549 dx * vc_data.font.width as i32, 550 dy * vc_data.font.height as i32, 551 width * vc_data.font.width, 552 height * vc_data.font.height, 553 sx * vc_data.font.width as i32, 554 sy * vc_data.font.height as i32, 555 ); 556 557 self.fb().fb_copyarea(area) 558 } 559 560 fn clear( 561 &self, 562 vc_data: &VirtualConsoleData, 563 sy: u32, 564 sx: u32, 565 height: u32, 566 width: u32, 567 ) -> Result<(), SystemError> { 568 let region = FillRectData::new( 569 sx * vc_data.font.width, 570 sy * vc_data.font.height, 571 width * vc_data.font.width, 572 height * vc_data.font.height, 573 self.get_color(vc_data, vc_data.erase_char, false), 574 FillRectROP::Copy, 575 ); 576 577 self.fb().fb_fillrect(region)?; 578 579 Ok(()) 580 } 581 582 fn put_string( 583 &self, 584 vc_data: &VirtualConsoleData, 585 data: &[u16], 586 mut count: u32, 587 y: u32, 588 x: u32, 589 fg: u32, 590 bg: u32, 591 ) -> Result<(), SystemError> { 592 // 向上取整 593 let width = (vc_data.font.width + 7) / 8; 594 let cellsize = width * vc_data.font.height; 595 let fb_info = self.fb(); 596 // 一次能输出的最大字数,避免帧缓冲区溢出 597 let max_cnt = (fb_info.current_fb_var().xres * fb_info.current_fb_var().yres) / cellsize; 598 let attr = FbConAttr::get_attr(data[0], fb_info.color_depth()); 599 600 let mut image = FbImage { 601 x: x * vc_data.font.width, 602 y: y * vc_data.font.height, 603 width: 0, 604 height: vc_data.font.height, 605 fg, 606 bg, 607 depth: 1, 608 data: Default::default(), 609 }; 610 611 image.data.resize(cellsize as usize * count as usize, 0); 612 613 while count > 0 { 614 let cnt = count.min(max_cnt); 615 616 image.width = vc_data.font.width * cnt; 617 618 self.bit_put_string(vc_data, data, attr, cnt, cellsize, &mut image); 619 620 image.x += cnt * vc_data.font.width; 621 count -= cnt; 622 } 623 624 Ok(()) 625 } 626 627 fn fbcon_data(&self) -> SpinLockGuard<super::FrameBufferConsoleData> { 628 self.fbcon_data.lock() 629 } 630 631 fn cursor(&self, vc_data: &VirtualConsoleData, op: CursorOperation, fg: u32, bg: u32) { 632 let mut fbcon_data = self.fbcon_data(); 633 let fb_info = self.fb(); 634 let mut cursor = FbCursor::default(); 635 let charmask = if vc_data.hi_font_mask != 0 { 636 0x1ff 637 } else { 638 0xff 639 }; 640 641 // 向上取整 642 let w = (vc_data.font.width + 7) / 8; 643 let y = fbcon_data.display.real_y(vc_data.state.y as u32); 644 645 let c = vc_data.screen_buf[vc_data.pos]; 646 let attr = FbConAttr::get_attr(c, fb_info.color_depth()); 647 let char_offset = (c as usize & charmask) * ((w * vc_data.font.height) as usize); 648 649 if fbcon_data.cursor_state.image.data != &vc_data.font.data[char_offset..] 650 || fbcon_data.cursor_reset 651 { 652 fbcon_data.cursor_state.image.data = vc_data.font.data[char_offset..].to_vec(); 653 cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETIMAGE); 654 } 655 656 if !attr.is_empty() { 657 fbcon_data 658 .cursor_data 659 .resize(w as usize * vc_data.font.height as usize, 0); 660 661 attr.update_attr( 662 &mut fbcon_data.cursor_data, 663 &vc_data.font.data[char_offset..], 664 vc_data, 665 ); 666 } 667 668 if fbcon_data.cursor_state.image.fg != fg 669 || fbcon_data.cursor_state.image.bg != bg 670 || fbcon_data.cursor_reset 671 { 672 fbcon_data.cursor_state.image.fg = fg; 673 fbcon_data.cursor_state.image.bg = bg; 674 cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETCMAP); 675 } 676 677 if fbcon_data.cursor_state.image.x != (vc_data.font.width * vc_data.state.x as u32) 678 || fbcon_data.cursor_state.image.y != (vc_data.font.height * y) 679 || fbcon_data.cursor_reset 680 { 681 fbcon_data.cursor_state.image.x = vc_data.font.width * vc_data.state.x as u32; 682 fbcon_data.cursor_state.image.y = vc_data.font.height * y; 683 cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETPOS); 684 } 685 686 if fbcon_data.cursor_state.image.height != vc_data.font.height 687 || fbcon_data.cursor_state.image.width != vc_data.font.width 688 || fbcon_data.cursor_reset 689 { 690 fbcon_data.cursor_state.image.height = vc_data.font.height; 691 fbcon_data.cursor_state.image.width = vc_data.font.width; 692 cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETSIZE); 693 } 694 695 if fbcon_data.cursor_state.hot_x > 0 696 || fbcon_data.cursor_state.hot_y > 0 697 || fbcon_data.cursor_reset 698 { 699 fbcon_data.cursor_state.hot_x = 0; 700 cursor.hot_y = 0; 701 cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETHOT); 702 } 703 704 if cursor.set_mode.contains(FbCursorSetMode::FB_CUR_SETSIZE) 705 || vc_data.cursor_type != fbcon_data.display.cursor_shape 706 || fbcon_data.cursor_state.mask.is_empty() 707 || fbcon_data.cursor_reset 708 { 709 fbcon_data.display.cursor_shape = vc_data.cursor_type; 710 cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETSHAPE); 711 712 let cur_height; 713 match fbcon_data.display.cursor_shape.cursor_size() { 714 VcCursor::CUR_NONE => { 715 cur_height = 0; 716 } 717 VcCursor::CUR_UNDERLINE => { 718 if vc_data.font.height < 10 { 719 cur_height = 1; 720 } else { 721 cur_height = 2; 722 } 723 } 724 VcCursor::CUR_LOWER_THIRD => { 725 cur_height = vc_data.font.height / 3; 726 } 727 VcCursor::CUR_LOWER_HALF => { 728 cur_height = vc_data.font.height >> 1; 729 } 730 VcCursor::CUR_TWO_THIRDS => { 731 cur_height = (vc_data.font.height << 1) / 3; 732 } 733 _ => { 734 cur_height = vc_data.font.height; 735 } 736 } 737 738 // 表示空白部分 739 let mut size = (vc_data.font.height - cur_height) * w; 740 while size > 0 { 741 size -= 1; 742 fbcon_data.cursor_state.mask.push(0x00); 743 } 744 size = cur_height * w; 745 // 表示光标显示部分 746 while size > 0 { 747 size -= 1; 748 fbcon_data.cursor_state.mask.push(0xff); 749 } 750 } 751 752 match op { 753 CursorOperation::Erase => { 754 fbcon_data.cursor_state.enable = false; 755 } 756 _ => { 757 fbcon_data.cursor_state.enable = !vc_data.cursor_type.contains(VcCursor::CUR_SW); 758 } 759 } 760 761 if !attr.is_empty() { 762 cursor.image.data = fbcon_data.cursor_data.clone(); 763 } else { 764 cursor.image.data = vc_data.font.data 765 [char_offset..char_offset + (w as usize * vc_data.font.height as usize)] 766 .to_vec(); 767 } 768 cursor.image.fg = fbcon_data.cursor_state.image.fg; 769 cursor.image.bg = fbcon_data.cursor_state.image.bg; 770 cursor.image.x = fbcon_data.cursor_state.image.x; 771 cursor.image.y = fbcon_data.cursor_state.image.y; 772 cursor.image.height = fbcon_data.cursor_state.image.height; 773 cursor.image.width = fbcon_data.cursor_state.image.width; 774 cursor.hot_x = fbcon_data.cursor_state.hot_x; 775 cursor.hot_y = fbcon_data.cursor_state.hot_y; 776 cursor.mask = fbcon_data.cursor_state.mask.clone(); 777 cursor.enable = fbcon_data.cursor_state.enable; 778 cursor.image.depth = 1; 779 cursor.rop = true; 780 781 if fb_info.fb_cursor(&cursor).is_err() { 782 let _ = fb_info.soft_cursor(cursor); 783 } 784 785 fbcon_data.cursor_reset = false; 786 } 787 } 788