1. 完善发送的逻辑,使用状态机和鼠标以及pip_file一同进行管理
2. 自测已可正常发送接收 3. atime的管理使用write还是read去更新需要使用宏来控制。目前已coding完毕等待调试
This commit is contained in:
parent
3303c3b208
commit
ed30319430
|
@ -27,3 +27,7 @@ dirs = "4.0"
|
|||
|
||||
[profile.test]
|
||||
env = { "RUST_LOG" = "debug" }
|
||||
|
||||
|
||||
[features]
|
||||
atime_read = []
|
301
src/lib.rs
301
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<MsgSyncPipe, Box<dyn Error>> {
|
||||
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) {
|
||||
/* 请求发送消息,向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
|
||||
// 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);
|
||||
#[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.get_update();
|
||||
}
|
||||
self.status_sync();
|
||||
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() {
|
||||
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<String, Box<dyn Error>> {
|
||||
fn get_producer_pipe() -> Result<(String,String), Box<dyn Error>> {
|
||||
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<Mutex<ClipboardSync>>) {
|
|||
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<Mutex<ClipboardSync>>) {
|
|||
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<Mutex<ClipboardSync>>) {
|
|||
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<Mutex<ClipboardSync>>) {
|
|||
}
|
||||
}
|
||||
|
||||
// 同步获取消息的线程
|
||||
async fn msg_sender(ctx:Arc<Mutex<ClipboardSync>>) {
|
||||
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<Mutex<ClipboardSync>>) {
|
|||
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<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{
|
||||
// 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 => {
|
||||
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;
|
||||
},
|
||||
// 当前处于窗口边缘,尝试切换到发送消息的逻辑:等待对方请求发送
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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 {
|
||||
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::<String>::new()
|
||||
}
|
||||
},
|
||||
None => {
|
||||
log::info!("clipboard is not string, ignore it");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user