Yazi Adaptor代码

作用

当前的作用是作为命令行终端中的图片显示的适配器,后面可能会引入更多的适配器?

Ueberzug

Ueberzug 是一个用于在终端显示图片的工具

引入的crate及其作用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//用于简化错误的处理,其中的`bail`宏,用于提早返回错误,并包含一个提供给`bail`的错误信息。
use anyhow::{bail, Result};
// 用于获取图片尺寸
use imagesize::ImageSize;
// 布局处理工具,用于定义显示图片的矩形区域大小
use ratatui::layout::Rect;
// 异步相关库
use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, UnboundedSender}};
// 用于打log
use tracing::debug;

静态变量及其作用

1
static DEMON: RoCell<Option<UnboundedSender<Option<(PathBuf, Rect)>>>> = RoCell::new();

上面的代码定义了一个静态变量DEMON,用于存储发送图片,显示命令的通道。这里的RoCell是一个只读Read-onlyCell,用于存储初始化时消耗大的不可变静态对象。

Ueberzug的相关方法

start

如果不需要Ueberzug,则直接用None初始化静态变量DEMON,否则,进入Ueberzug的初始化逻辑。

当前终端不属于Self::Kitty | Self::KittyOld | Self::Iterm2 | Self::Sixel这个集合时,需要初始化Ueberzug,具体而言实际上是X11,Wayland,Chafa这三种情况。

在初始化Ueberzug的过程中,实际上是创建了一个Ueberzug的进程,但是使用的并不是标准库的CommandChild而是异步版本的相关内容。

之后创建一个无界的多生产者,单消费者的通道,这里的无界的通道在中低负载的情况,因为减少了等待队列变得可用的时间,性能优于有界的通道,但是在高负载的情况下,有可能导致内存问题。

这个通道的消费者 接收端 ,被传入一个新的异步任务中,这个异步任务循环从接收端获取命令,并发送到Ueberzug进程中。每次发到子进程前,都会检查进程是否仍存在,若不存在,则新建一个Ueberzug进程。

而通道的生产者,则用于初始化上面的静态变量DEMON

image_show

这个方法的作用是,对给定的图片进行判断,来决定是缩放图片还是保持原样。

几个我觉得比较负责的语句是

模式匹配拿到tx
1
2
3
4
5
6
if let Some(tx) = &*DEMON {
			tx.send(Some((path.to_path_buf(), rect)))?;
			Adaptor::shown_store(rect, (0, 0));
		} else {
			bail!("uninitialized ueberzug");
		}

注意其中的DEMONRoCell的定义如下,

1
2
3
static DEMON: RoCell<Option<UnboundedSender<Option<(PathBuf, Rect)>>>> = RoCell::new();

struct RoCell<T>(UnsafeCell<Option<T>>);

这里的 &*DEMON的解释是,首先对DEMON进行解引用,获取RoCell中的Option<UnboundedSender<Option<(PathBuf, Rect)>>>,因为RoCell实现了Deref的Trait,所以可以这么做,然后再通过&获取到Option<UnboundedSender<Option<(PathBuf, Rect)>>>的引用,最后通过Some(tx),将其中的内容模式匹配到tx中。

获取图片尺寸
1
2
let ImageSize { width: w, height: h } =
			tokio::task::spawn_blocking(move || imagesize::size(path)).await??;

spawn_blocking是用于在异步运行时中,执行一个阻塞的操作,而这个阻塞操作是imagesize::size(path),这里的move关键字的作用是获取path的所有权,从而安全的使用该变量。

这句代码的一个特点是有两个?,其中,第一个?是用来处理spawn_blocking 返回的JoinHandle中可能存在的错误,而第二个?则是用于处理闭包中的imagesize::size(path)的返回值,用于传播这个错误。

最后,let ImageSize { width: w, height: h } 这部分代码,是通过模式匹配来解构实例,最后的结果被赋值到了wh这两个字段。

image_erase

擦除图片,通过在tx上传一个None来实现。

creat_demon

封装了创建Ueberzug进程的代码。

adjust_rect

通过yazi-config中的值,调整图片显示区域

send_command

Ueberzug进行通信的一层封装,实际用于画图和清除图片的代码都是通过调用send_command来通知Ueberzug进程的。

Licensed under CC BY-NC-SA 4.0