From ed303194306e948f288cab1cfddf5cd3b4b8c1cb Mon Sep 17 00:00:00 2001 From: Begild Date: Thu, 19 Sep 2024 08:44:31 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=AE=8C=E5=96=84=E5=8F=91=E9=80=81?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BD=BF=E7=94=A8=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=9C=BA=E5=92=8C=E9=BC=A0=E6=A0=87=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?pip=5Ffile=E4=B8=80=E5=90=8C=E8=BF=9B=E8=A1=8C=E7=AE=A1?= =?UTF-8?q?=E7=90=86=202.=20=E8=87=AA=E6=B5=8B=E5=B7=B2=E5=8F=AF=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E5=8F=91=E9=80=81=E6=8E=A5=E6=94=B6=203.=20atime?= =?UTF-8?q?=E7=9A=84=E7=AE=A1=E7=90=86=E4=BD=BF=E7=94=A8write=E8=BF=98?= =?UTF-8?q?=E6=98=AFread=E5=8E=BB=E6=9B=B4=E6=96=B0=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=AE=8F=E6=9D=A5=E6=8E=A7=E5=88=B6=E3=80=82?= =?UTF-8?q?=E7=9B=AE=E5=89=8D=E5=B7=B2coding=E5=AE=8C=E6=AF=95=E7=AD=89?= =?UTF-8?q?=E5=BE=85=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 4 + src/lib.rs | 323 ++++++++++++++++++++++++++++++++++++++------- src/sys_res/mod.rs | 4 +- 3 files changed, 278 insertions(+), 53 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fe86615..2925093 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,3 +27,7 @@ dirs = "4.0" [profile.test] env = { "RUST_LOG" = "debug" } + + +[features] +atime_read = [] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 858d832..178a4db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,7 +129,9 @@ pub struct ClipboardSync { clip: wclip::Wclip, cfg: Config, pipe: MsgSyncPipe, + cursor: CursorCtrl, 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"; @@ -157,6 +159,7 @@ pub enum Character { } struct MsgSyncPipe { pub pipe_file: String, + pub status_file: String, atime: Duration, } @@ -167,13 +170,14 @@ struct MsgSyncPipe { */ impl MsgSyncPipe { pub fn new(c:&Character) -> Result> { - let pipe_file = match c { + let (pipe_file,status_file) = match c { Character::Producer => Self::get_producer_pipe()?, Character::Consumer => Self::get_consumer_pipe()?, }; let atime = Self::get_pipe_atime(&pipe_file); Ok(MsgSyncPipe { pipe_file, + status_file, 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); log::trace!("pipe file access time: {:?}", atime); if atime > self.atime { @@ -198,38 +202,82 @@ impl MsgSyncPipe { return false; } - pub fn set_update(&mut self) { - // read pipe file to update access time - // let mut file = File::options().read(true).open(&self.pipe_file).unwrap(); - // let mut buffer = Vec::new(); - // log::trace!("try update pipe file access time"); - // let _ = file.read_to_end(&mut buffer); - let mut file = File::options().write(true).open(&self.pipe_file).unwrap(); - let _ = file.write("sstar_l0l3clip_sync".as_bytes()); + /* 请求发送消息,向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); - self.get_update(); + } + /* 检查对方是否已经发送了消息 */ + 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 + #[cfg(features = "atime_read")] + { + let mut file = File::options().read(true).open(&self.pipe_file).unwrap(); + 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 _ = file.write("sstar_l0l3clip_sync".as_bytes()); + drop(file); + } + self.status_sync(); log::trace!("pipe file access time updated: {:?}", self.atime); } - fn get_consumer_pipe() -> Result> { + fn get_consumer_pipe() -> Result<(String, String), Box> { let path = match dirs::home_dir() { Some(path) => path, 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() { - let mut file = fs::OpenOptions::new().create(true).write(true).open(&path)?; - let _ = file.write("sstar_l0l3clip_sync".as_bytes()); + fs::create_dir(&path)?; + 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> { + fn get_producer_pipe() -> Result<(String,String), Box> { let path = match dirs::home_dir() { Some(path) => path, 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 _ = path.next(); //ignore driver letter let sub_path = path.next().unwrap().to_string(); @@ -237,10 +285,12 @@ impl MsgSyncPipe { let sub_path = format!("{}:{}", drive, sub_path); let sub_path = sub_path.replace("baoyucang", "BYC10"); log::trace!("trying to find pipe file in {}", sub_path); - if Path::new(&sub_path).exists() { - return Ok(sub_path); + let sync_pipe = Path::new(&sub_path).join("sync_pipe"); + 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()) } } @@ -256,6 +306,7 @@ async fn msg_reciver(ctx:Arc>) { prog_name == vm_exe_name } let mut state_time = time::Instant::now(); + ctx.pipe.status_sync(); loop{ // log::trace!("current state is {:?}", sta); match sta{ @@ -275,7 +326,7 @@ async fn msg_reciver(ctx:Arc>) { if !on_vm() { turn_time = 0; //请求远端发送消息 - ctx.pipe.set_update(); + ctx.pipe.request_msg(); sta = MsgSyncState::WaitingSendMsg; state_time = time::Instant::now(); log::trace!("waiting for remote send msg..."); @@ -284,10 +335,16 @@ async fn msg_reciver(ctx:Arc>) { MsgSyncState::WaitingSendMsg => { turn_time = 50; // 如果这里检测到了对方更新了文件状态,说明其已经发送完毕消息,我们开始时接收。 - if ctx.pipe.get_update() { + if ctx.pipe.is_sent_msg() { + ctx.pipe.clean_request(); sta = MsgSyncState::WaitingRecMsg; turn_time = 0; } else { + if on_vm() { + // 如果鼠标移到了VM窗口,那么就切换 + sta = MsgSyncState::FocusOnVM; + turn_time = 0; + } //最多等待1秒钟,如果超过1秒钟还没收到消息,那么就切换到空闲状态 if time::Instant::now() - state_time > Duration::new(1, 0) { sta = MsgSyncState::Idle; @@ -309,14 +366,38 @@ async fn msg_reciver(ctx:Arc>) { } } -// 同步获取消息的线程 -async fn msg_sender(ctx:Arc>) { - let mut ctx = ctx.lock().await; - let mut sta = MsgSyncState::Idle; - let mut turn_time; - fn on_edge() -> bool { - let (width, height) = sys_res::screen_size(); +struct CursorCtrl{ + pub x: i32, + pub y: i32, + is_edge: bool, + state: CursorState, +} + +#[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(); + 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; if x < margin || y< margin || x > width - margin || y > height - margin { return true @@ -324,43 +405,172 @@ async fn msg_sender(ctx:Arc>) { log::trace!("cursor pos:({},{})", x, y); 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>) { + let mut ctx = ctx.lock().await; + let mut sta = MsgSyncState::Idle; + let mut turn_time; + let mut state_time = time::Instant::now(); loop{ // log::trace!("current state is {:?}", sta); match sta{ MsgSyncState::Idle => { turn_time = 0; // 更新下最新的文件状态,保证后续判断准确 - ctx.pipe.get_update(); // refresh pipe file status + ctx.pipe.status_sync(); // refresh pipe file status sta = MsgSyncState::FocusOnVM; }, MsgSyncState::FocusOnVM => { - turn_time = 100; - // 当前处于窗口边缘,尝试切换到发送消息的逻辑:等待对方请求发送 - if on_edge() { + 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; + 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; + }, + // 当前处于窗口边缘,尝试切换到发送消息的逻辑:等待对方请求发送 + 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; turn_time = 0; } }, MsgSyncState::WaitingSendMsg => { - turn_time = 50; - // 如果对方需要发送消息那么他会更新atime,这里就尝试发送消息 - if ctx.pipe.get_update() { - let _ = ctx.clip2msg().await; - log::trace!("send msg done"); - turn_time = 0; - sta = MsgSyncState::WaitingRecMsg; - ctx.pipe.set_update();//sent ok set update flag - // 如果文件没更新说明不需要发送消息,并且鼠标也不在边缘说明重新移回VM窗口内了,可以切换到等待状态 - } else if !on_edge() { - turn_time = 0; - sta = MsgSyncState::Idle; - } + let _ = ctx.clip2msg().await; + log::trace!("send msg done"); + turn_time = 0; + sta = MsgSyncState::WaitingRecMsg; + ctx.pipe.reply_msg(); }, MsgSyncState::WaitingRecMsg => { turn_time = 0; sta = MsgSyncState::Idle; // 发送消息后鼠标还是保持在边缘说明当前鼠标还是在VM窗口外面,需要等待鼠标移动到VM窗口内,然后再发送消息 - if on_edge() { + let cursor = ctx.cursor.get_sta(); + if cursor == CursorState::Standby || + cursor == CursorState::StandbyEdge || + cursor == CursorState::StandbyCenter { turn_time = 100; sta = MsgSyncState::WaitingRecMsg; } @@ -400,7 +610,9 @@ impl ClipboardSync { clip: wclip::Wclip::new(), cfg: Config::new(info_file)?, pipe: MsgSyncPipe::new(c)?, + cursor: CursorCtrl::new(), characters: c.clone(), + recent_msg: String::new(), }) } @@ -666,9 +878,18 @@ impl ClipboardSync { } pub async fn clip2msg(&mut self) { let msg = match self.clip.get() { - Some(msg) => match self.split_msg(&msg).await { - Ok(msgs) => msgs, - Err(_) => return, + Some(msg) => { + let split = match self.split_msg(&msg).await { + Ok(msgs) => msgs, + Err(_) => return, + }; + if self.recent_msg != msg { + self.recent_msg = msg; + split + } else { + log::info!("msg is same as last time, ignore it"); + Vec::::new() + } }, None => { log::info!("clipboard is not string, ignore it"); diff --git a/src/sys_res/mod.rs b/src/sys_res/mod.rs index 07809a5..a687f04 100755 --- a/src/sys_res/mod.rs +++ b/src/sys_res/mod.rs @@ -39,7 +39,7 @@ pub fn get_foredround_window_rect() -> Option<(i32, i32, i32, i32)> { println!("GetWindowRect: {:?}", rect); Some((rect.left, rect.top, rect.right, rect.bottom)) } else { - log::error!("cant get foregtound window rect"); + log::error!("Cant get foreground window rect"); None } } @@ -58,7 +58,7 @@ pub fn get_foredround_window_name() -> String { let process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, process_id); 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(); }