1. 完善发送的逻辑,使用状态机和鼠标以及pip_file一同进行管理

2. 自测已可正常发送接收
3. atime的管理使用write还是read去更新需要使用宏来控制。目前已coding完毕等待调试
This commit is contained in:
Ekko.bao 2024-09-19 08:44:31 +08:00
parent 3303c3b208
commit ed30319430
3 changed files with 278 additions and 53 deletions

View File

@ -27,3 +27,7 @@ dirs = "4.0"
[profile.test] [profile.test]
env = { "RUST_LOG" = "debug" } env = { "RUST_LOG" = "debug" }
[features]
atime_read = []

View File

@ -129,7 +129,9 @@ pub struct ClipboardSync {
clip: wclip::Wclip, clip: wclip::Wclip,
cfg: Config, cfg: Config,
pipe: MsgSyncPipe, pipe: MsgSyncPipe,
cursor: CursorCtrl,
characters: Character, characters: Character,
recent_msg: String,
} }
const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42"; const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42";
@ -157,6 +159,7 @@ pub enum Character {
} }
struct MsgSyncPipe { struct MsgSyncPipe {
pub pipe_file: String, pub pipe_file: String,
pub status_file: String,
atime: Duration, atime: Duration,
} }
@ -167,13 +170,14 @@ struct MsgSyncPipe {
*/ */
impl MsgSyncPipe { impl MsgSyncPipe {
pub fn new(c:&Character) -> Result<MsgSyncPipe, Box<dyn Error>> { pub fn new(c:&Character) -> Result<MsgSyncPipe, Box<dyn Error>> {
let pipe_file = match c { let (pipe_file,status_file) = match c {
Character::Producer => Self::get_producer_pipe()?, Character::Producer => Self::get_producer_pipe()?,
Character::Consumer => Self::get_consumer_pipe()?, Character::Consumer => Self::get_consumer_pipe()?,
}; };
let atime = Self::get_pipe_atime(&pipe_file); let atime = Self::get_pipe_atime(&pipe_file);
Ok(MsgSyncPipe { Ok(MsgSyncPipe {
pipe_file, pipe_file,
status_file,
atime, atime,
}) })
} }
@ -188,7 +192,7 @@ impl MsgSyncPipe {
} }
} }
pub fn get_update(&mut self) -> bool { pub fn is_atime_update(&mut self) -> bool {
let atime = Self::get_pipe_atime(&self.pipe_file); let atime = Self::get_pipe_atime(&self.pipe_file);
log::trace!("pipe file access time: {:?}", atime); log::trace!("pipe file access time: {:?}", atime);
if atime > self.atime { if atime > self.atime {
@ -198,38 +202,82 @@ impl MsgSyncPipe {
return false; return false;
} }
pub fn set_update(&mut self) { /* 请求发送消息向status文件写入请求状态 */
pub fn request_msg(&mut self) {
let mut file = File::options().write(true).open(&self.status_file).unwrap();
let _ = file.write("REQUEST".as_bytes());
drop(file);
}
/* 检查对方是否已经发送了消息 */
pub fn is_sent_msg(&mut self) -> bool {
self.is_atime_update()
}
/* 清除发送消息的状态,在知道对方已经发送了消息的时候进行清除 */
pub fn clean_request(&mut self) {
let mut file = File::options().write(true).open(&self.status_file).unwrap();
let _ = file.write("IDLE".as_bytes());
drop(file);
}
/* sync一下目前的pipe的atime用来同步一下两边记录的值 */
pub fn status_sync(&mut self) -> bool {
self.is_atime_update()
}
pub fn is_request_msg(&mut self) -> bool {
let mut file = File::options().read(true).open(&self.status_file).unwrap();
let mut buffer = Vec::new();
let _ = file.read_to_end(&mut buffer);
let status = String::from_utf8(buffer).unwrap();
if status == "REQUEST" {
return true;
}
return false;
}
pub fn reply_msg(&mut self) {
// read pipe file to update access time // read pipe file to update access time
// let mut file = File::options().read(true).open(&self.pipe_file).unwrap(); #[cfg(features = "atime_read")]
// let mut buffer = Vec::new(); {
// log::trace!("try update pipe file access time"); let mut file = File::options().read(true).open(&self.pipe_file).unwrap();
// let _ = file.read_to_end(&mut buffer); let mut buffer = Vec::new();
let _ = file.read_to_end(&mut buffer);
}
#[cfg(not(features = "atime_read"))]
{
let mut file = File::options().write(true).open(&self.pipe_file).unwrap(); let mut file = File::options().write(true).open(&self.pipe_file).unwrap();
let _ = file.write("sstar_l0l3clip_sync".as_bytes()); let _ = file.write("sstar_l0l3clip_sync".as_bytes());
drop(file); drop(file);
self.get_update(); }
self.status_sync();
log::trace!("pipe file access time updated: {:?}", self.atime); log::trace!("pipe file access time updated: {:?}", self.atime);
} }
fn get_consumer_pipe() -> Result<String, Box<dyn Error>> { fn get_consumer_pipe() -> Result<(String, String), Box<dyn Error>> {
let path = match dirs::home_dir() { let path = match dirs::home_dir() {
Some(path) => path, Some(path) => path,
None => return Err("can not get user HOME dir".into()), None => return Err("can not get user HOME dir".into()),
}; };
let path = path.join(".sstar_l0l3clip_sync"); // 这里希望通过两个文件来实现同步一个是共享的pipe 标志文件,一个是请求状态文件。
// 共享的pipe标志文件用于标识有新的消息需要发送请求状态文件用于记录当前请求状态。
let path = path.join("sstar_l0l3clip_sync");
let sync_pipe = path.join("sync_pipe");
let req_status = path.join("req_status");
if !path.exists() { if !path.exists() {
let mut file = fs::OpenOptions::new().create(true).write(true).open(&path)?; fs::create_dir(&path)?;
let _ = file.write("sstar_l0l3clip_sync".as_bytes()); let mut file = fs::OpenOptions::new().create(true).write(true).open(&sync_pipe)?;
let _ = file.write("sync_pipe".as_bytes());
let mut file = fs::OpenOptions::new().create(true).write(true).open(&req_status)?;
let _ = file.write("IDLE".as_bytes());
} }
Ok(path.to_str().unwrap().to_string()) Ok((sync_pipe.to_str().unwrap().to_string(), req_status.to_str().unwrap().to_string()))
} }
fn get_producer_pipe() -> Result<String, Box<dyn Error>> { fn get_producer_pipe() -> Result<(String,String), Box<dyn Error>> {
let path = match dirs::home_dir() { let path = match dirs::home_dir() {
Some(path) => path, Some(path) => path,
None => return Err("can not get home dir".into()), None => return Err("can not get home dir".into()),
}; };
let path = path.join(".sstar_l0l3clip_sync").to_str().unwrap().to_string(); let path = path.join("sstar_l0l3clip_sync").to_str().unwrap().to_string();
// 切割一下将盘符去掉留下剩下的部分方便匹配
let mut path = path.split(':').into_iter(); let mut path = path.split(':').into_iter();
let _ = path.next(); //ignore driver letter let _ = path.next(); //ignore driver letter
let sub_path = path.next().unwrap().to_string(); let sub_path = path.next().unwrap().to_string();
@ -237,10 +285,12 @@ impl MsgSyncPipe {
let sub_path = format!("{}:{}", drive, sub_path); let sub_path = format!("{}:{}", drive, sub_path);
let sub_path = sub_path.replace("baoyucang", "BYC10"); let sub_path = sub_path.replace("baoyucang", "BYC10");
log::trace!("trying to find pipe file in {}", sub_path); log::trace!("trying to find pipe file in {}", sub_path);
if Path::new(&sub_path).exists() { let sync_pipe = Path::new(&sub_path).join("sync_pipe");
return Ok(sub_path); let req_status = Path::new(&sub_path).join("req_status");
} if req_status.exists() && sync_pipe.exists(){
return Ok((sync_pipe.to_str().unwrap().to_string(), req_status.to_str().unwrap().to_string()))
} }
};
return Err("You should mapping l0 c:\\ to l3".into()) return Err("You should mapping l0 c:\\ to l3".into())
} }
} }
@ -256,6 +306,7 @@ async fn msg_reciver(ctx:Arc<Mutex<ClipboardSync>>) {
prog_name == vm_exe_name prog_name == vm_exe_name
} }
let mut state_time = time::Instant::now(); let mut state_time = time::Instant::now();
ctx.pipe.status_sync();
loop{ loop{
// log::trace!("current state is {:?}", sta); // log::trace!("current state is {:?}", sta);
match sta{ match sta{
@ -275,7 +326,7 @@ async fn msg_reciver(ctx:Arc<Mutex<ClipboardSync>>) {
if !on_vm() { if !on_vm() {
turn_time = 0; turn_time = 0;
//请求远端发送消息 //请求远端发送消息
ctx.pipe.set_update(); ctx.pipe.request_msg();
sta = MsgSyncState::WaitingSendMsg; sta = MsgSyncState::WaitingSendMsg;
state_time = time::Instant::now(); state_time = time::Instant::now();
log::trace!("waiting for remote send msg..."); log::trace!("waiting for remote send msg...");
@ -284,10 +335,16 @@ async fn msg_reciver(ctx:Arc<Mutex<ClipboardSync>>) {
MsgSyncState::WaitingSendMsg => { MsgSyncState::WaitingSendMsg => {
turn_time = 50; turn_time = 50;
// 如果这里检测到了对方更新了文件状态,说明其已经发送完毕消息,我们开始时接收。 // 如果这里检测到了对方更新了文件状态,说明其已经发送完毕消息,我们开始时接收。
if ctx.pipe.get_update() { if ctx.pipe.is_sent_msg() {
ctx.pipe.clean_request();
sta = MsgSyncState::WaitingRecMsg; sta = MsgSyncState::WaitingRecMsg;
turn_time = 0; turn_time = 0;
} else { } else {
if on_vm() {
// 如果鼠标移到了VM窗口那么就切换
sta = MsgSyncState::FocusOnVM;
turn_time = 0;
}
//最多等待1秒钟如果超过1秒钟还没收到消息那么就切换到空闲状态 //最多等待1秒钟如果超过1秒钟还没收到消息那么就切换到空闲状态
if time::Instant::now() - state_time > Duration::new(1, 0) { if time::Instant::now() - state_time > Duration::new(1, 0) {
sta = MsgSyncState::Idle; sta = MsgSyncState::Idle;
@ -309,14 +366,38 @@ async fn msg_reciver(ctx:Arc<Mutex<ClipboardSync>>) {
} }
} }
// 同步获取消息的线程 struct CursorCtrl{
async fn msg_sender(ctx:Arc<Mutex<ClipboardSync>>) { pub x: i32,
let mut ctx = ctx.lock().await; pub y: i32,
let mut sta = MsgSyncState::Idle; is_edge: bool,
let mut turn_time; state: CursorState,
fn on_edge() -> bool { }
let (width, height) = sys_res::screen_size();
#[derive(PartialEq,Copy, Clone)]
enum CursorState {
Standby,
StandbyEdge,
StandbyCenter,
Move2Edge,
Move2Center,
MovingCenter,
MovingEdge,
}
impl CursorCtrl{
pub fn new() -> Self {
let (x,y) = sys_res::cursor_pos(); let (x,y) = sys_res::cursor_pos();
Self {
x,
y,
is_edge: false,
state: CursorState::Standby,
}
}
pub fn on_edge(x:i32, y:i32) -> bool {
let (width, height) = sys_res::screen_size();
// let (x,y) = sys_res::cursor_pos();
let margin = 20; let margin = 20;
if x < margin || y< margin || x > width - margin || y > height - margin { if x < margin || y< margin || x > width - margin || y > height - margin {
return true return true
@ -324,43 +405,172 @@ async fn msg_sender(ctx:Arc<Mutex<ClipboardSync>>) {
log::trace!("cursor pos:({},{})", x, y); log::trace!("cursor pos:({},{})", x, y);
false false
} }
pub fn get_sta(&mut self) -> CursorState {
let (x,y) = sys_res::cursor_pos();
let mut is_moving = false;
if x != self.x || y != self.y {
is_moving = true;
self.is_edge = CursorCtrl::on_edge(x,y);
self.x = x;
self.y = y;
}
self.state = match self.state {
CursorState::Standby => {
if is_moving {
if self.is_edge {
CursorState::MovingEdge
} else {
CursorState::MovingCenter
}
} else {
if self.is_edge {
CursorState::StandbyEdge
} else {
CursorState::StandbyCenter
}
}
},
CursorState::StandbyEdge => {
if is_moving {
if self.is_edge {
CursorState::MovingEdge
} else {
CursorState::Move2Center
}
} else {
CursorState::StandbyEdge
}
},
CursorState::StandbyCenter => {
if is_moving {
if self.is_edge {
CursorState::Move2Edge
} else {
CursorState::MovingCenter
}
} else {
CursorState::StandbyCenter
}
},
CursorState::Move2Edge => {
if is_moving {
if self.is_edge {
CursorState::MovingEdge
} else {
CursorState::Move2Center
}
} else {
CursorState::StandbyEdge
}
},
CursorState::Move2Center => {
if is_moving {
if self.is_edge {
CursorState::Move2Edge
} else {
CursorState::MovingCenter
}
} else {
CursorState::StandbyCenter
}
},
CursorState::MovingCenter => {
if is_moving {
if self.is_edge {
CursorState::Move2Edge
} else {
CursorState::MovingCenter
}
} else {
CursorState::StandbyCenter
}
},
CursorState::MovingEdge => {
if is_moving {
if self.is_edge {
CursorState::MovingEdge
} else {
CursorState::Move2Center
}
} else {
CursorState::StandbyEdge
}
},
};
self.state
}
}
// 发送消息的线程
async fn msg_sender(ctx:Arc<Mutex<ClipboardSync>>) {
let mut ctx = ctx.lock().await;
let mut sta = MsgSyncState::Idle;
let mut turn_time;
let mut state_time = time::Instant::now();
loop{ loop{
// log::trace!("current state is {:?}", sta); // log::trace!("current state is {:?}", sta);
match sta{ match sta{
MsgSyncState::Idle => { MsgSyncState::Idle => {
turn_time = 0; turn_time = 0;
// 更新下最新的文件状态,保证后续判断准确 // 更新下最新的文件状态,保证后续判断准确
ctx.pipe.get_update(); // refresh pipe file status ctx.pipe.status_sync(); // refresh pipe file status
sta = MsgSyncState::FocusOnVM; sta = MsgSyncState::FocusOnVM;
}, },
MsgSyncState::FocusOnVM => { MsgSyncState::FocusOnVM => {
let cursor = ctx.cursor.get_sta();
match cursor {
CursorState::MovingCenter => {
turn_time = 200;
state_time = time::Instant::now();
},
CursorState::Move2Center => {
turn_time = 200;
},
CursorState::StandbyCenter => {
turn_time = 100; turn_time = 100;
if state_time.elapsed() > Duration::new(1, 0) {
turn_time = 200;
}
},
CursorState::MovingEdge => {
turn_time = 100;
state_time = time::Instant::now();
},
CursorState::Move2Edge => {
turn_time = 100;
},
// 当前处于窗口边缘,尝试切换到发送消息的逻辑:等待对方请求发送 // 当前处于窗口边缘,尝试切换到发送消息的逻辑:等待对方请求发送
if on_edge() { CursorState::StandbyEdge => {
turn_time = 50;
if state_time.elapsed() > Duration::new(1, 0) {
turn_time = 200;
}
},
_ => {
turn_time = 100;
}
}
if ctx.pipe.is_request_msg() {
sta = MsgSyncState::WaitingSendMsg; sta = MsgSyncState::WaitingSendMsg;
turn_time = 0; turn_time = 0;
} }
}, },
MsgSyncState::WaitingSendMsg => { MsgSyncState::WaitingSendMsg => {
turn_time = 50;
// 如果对方需要发送消息那么他会更新atime这里就尝试发送消息
if ctx.pipe.get_update() {
let _ = ctx.clip2msg().await; let _ = ctx.clip2msg().await;
log::trace!("send msg done"); log::trace!("send msg done");
turn_time = 0; turn_time = 0;
sta = MsgSyncState::WaitingRecMsg; sta = MsgSyncState::WaitingRecMsg;
ctx.pipe.set_update();//sent ok set update flag ctx.pipe.reply_msg();
// 如果文件没更新说明不需要发送消息并且鼠标也不在边缘说明重新移回VM窗口内了可以切换到等待状态
} else if !on_edge() {
turn_time = 0;
sta = MsgSyncState::Idle;
}
}, },
MsgSyncState::WaitingRecMsg => { MsgSyncState::WaitingRecMsg => {
turn_time = 0; turn_time = 0;
sta = MsgSyncState::Idle; sta = MsgSyncState::Idle;
// 发送消息后鼠标还是保持在边缘说明当前鼠标还是在VM窗口外面需要等待鼠标移动到VM窗口内然后再发送消息 // 发送消息后鼠标还是保持在边缘说明当前鼠标还是在VM窗口外面需要等待鼠标移动到VM窗口内然后再发送消息
if on_edge() { let cursor = ctx.cursor.get_sta();
if cursor == CursorState::Standby ||
cursor == CursorState::StandbyEdge ||
cursor == CursorState::StandbyCenter {
turn_time = 100; turn_time = 100;
sta = MsgSyncState::WaitingRecMsg; sta = MsgSyncState::WaitingRecMsg;
} }
@ -400,7 +610,9 @@ impl ClipboardSync {
clip: wclip::Wclip::new(), clip: wclip::Wclip::new(),
cfg: Config::new(info_file)?, cfg: Config::new(info_file)?,
pipe: MsgSyncPipe::new(c)?, pipe: MsgSyncPipe::new(c)?,
cursor: CursorCtrl::new(),
characters: c.clone(), characters: c.clone(),
recent_msg: String::new(),
}) })
} }
@ -666,9 +878,18 @@ impl ClipboardSync {
} }
pub async fn clip2msg(&mut self) { pub async fn clip2msg(&mut self) {
let msg = match self.clip.get() { let msg = match self.clip.get() {
Some(msg) => match self.split_msg(&msg).await { Some(msg) => {
let split = match self.split_msg(&msg).await {
Ok(msgs) => msgs, Ok(msgs) => msgs,
Err(_) => return, Err(_) => return,
};
if self.recent_msg != msg {
self.recent_msg = msg;
split
} else {
log::info!("msg is same as last time, ignore it");
Vec::<String>::new()
}
}, },
None => { None => {
log::info!("clipboard is not string, ignore it"); log::info!("clipboard is not string, ignore it");

View File

@ -39,7 +39,7 @@ pub fn get_foredround_window_rect() -> Option<(i32, i32, i32, i32)> {
println!("GetWindowRect: {:?}", rect); println!("GetWindowRect: {:?}", rect);
Some((rect.left, rect.top, rect.right, rect.bottom)) Some((rect.left, rect.top, rect.right, rect.bottom))
} else { } else {
log::error!("cant get foregtound window rect"); log::error!("Cant get foreground window rect");
None None
} }
} }
@ -58,7 +58,7 @@ pub fn get_foredround_window_name() -> String {
let process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, process_id); let process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, process_id);
if process_handle.is_null() { if process_handle.is_null() {
log::error!("Cant open foreground process with {}!", process_id); log::warn!("process {} cant process handle!", process_id);
return "".to_string(); return "".to_string();
} }