添加msg的提供者的逻辑

完成基础版本的交互逻辑已经具备同步消息的框架了。就是状态维护上有点瑕疵等待完善。
This commit is contained in:
Ekko.bao 2024-09-06 08:38:28 +08:00
parent 089bf1e3ac
commit 3303c3b208
5 changed files with 257 additions and 72 deletions

View File

@ -23,6 +23,7 @@ winapi = { version = "0.3.9", features = ["winuser", "psapi"] }
base64 = "0.21"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
dirs = "4.0"
[profile.test]
env = { "RUST_LOG" = "debug" }

View File

@ -1,6 +1,6 @@
{
"cookies": "",
"magic": "3",
"magic": "2022-10-20 11:25:09.92",
"password": "ZWtrb2Jhb19fX19fX19fXzlytojwgd3qVW7PNYs7Y9s=",
"user_name": "ekko.bao"
}

View File

@ -1,11 +1,9 @@
use log::Log;
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use rand::{random, Rng};
use rand::Rng;
use reqwest::header::HeaderMap;
use reqwest::{Client, Response};
use scraper::{Html, Selector};
use core::ascii;
use std::error::Error;
use std::io::{self, Write};
use std::time::Duration;
@ -14,10 +12,14 @@ use std::sync::Arc;
use tokio::{task, time};
use std::collections::BTreeMap;
extern crate log;
use serde_json::{self, to_string, Value};
use serde_json::{self, Value};
use serde::Serialize;
use std::fs::File;
use std::fs::{self, File};
use std::io::BufReader;
use dirs;
use std::path::Path;
use std::time::UNIX_EPOCH;
use std::io::Read;
pub mod wclip;
pub mod sys_res;
@ -126,6 +128,8 @@ pub struct ClipboardSync {
ip: String,
clip: wclip::Wclip,
cfg: Config,
pipe: MsgSyncPipe,
characters: Character,
}
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";
@ -140,43 +144,163 @@ pub struct ClipboardMsgs {
#[derive(Debug)]
enum MsgSyncState {
None,
ForceInVM,
NeedSync,
Idle,
FocusOnVM,
WaitingSendMsg,
WaitingRecMsg,
}
#[derive(Clone)]
pub enum Character {
Producer,
Consumer,
}
struct MsgSyncPipe {
pub pipe_file: String,
atime: Duration,
}
/*
* 使pipe文件的atime来同步消息状态
* 访pipe文件访使
* 访pipe文件发现atime被更新atime更新,使
*/
impl MsgSyncPipe {
pub fn new(c:&Character) -> Result<MsgSyncPipe, Box<dyn Error>> {
let pipe_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,
atime,
})
}
fn get_pipe_atime(path:&str) -> Duration {
let metadata = fs::metadata(path).unwrap();
// 获取最后访问时间
if let Ok(accessed) = metadata.accessed() {
accessed.duration_since(UNIX_EPOCH).unwrap()
} else {
Duration::new(0, 0)
}
}
pub fn get_update(&mut self) -> bool {
let atime = Self::get_pipe_atime(&self.pipe_file);
log::trace!("pipe file access time: {:?}", atime);
if atime > self.atime {
self.atime = atime;
return true;
}
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());
drop(file);
self.get_update();
log::trace!("pipe file access time updated: {:?}", self.atime);
}
fn get_consumer_pipe() -> Result<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");
if !path.exists() {
let mut file = fs::OpenOptions::new().create(true).write(true).open(&path)?;
let _ = file.write("sstar_l0l3clip_sync".as_bytes());
}
Ok(path.to_str().unwrap().to_string())
}
fn get_producer_pipe() -> Result<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 mut path = path.split(':').into_iter();
let _ = path.next(); //ignore driver letter
let sub_path = path.next().unwrap().to_string();
for drive in 'C'..='Z' {
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);
}
}
return Err("You should mapping l0 c:\\ to l3".into())
}
}
static VM_EXE_NAME:&str = "mstsc.exe";
// 同步获取消息的线程
async fn msg_sync(ctx:Arc<Mutex<ClipboardSync>>) {
let VM_EXE_NAME = "Code.exe".to_string();
async fn msg_reciver(ctx:Arc<Mutex<ClipboardSync>>) {
let mut ctx = ctx.lock().await;
let mut sta = MsgSyncState::None;
let mut sta = MsgSyncState::Idle;
let mut turn_time;
//let _ = ctx.update_msg().await;
loop{
log::trace!("current state is {:?}", sta);
match sta{
MsgSyncState::None => {
turn_time = 200;
fn on_vm() -> bool {
let vm_exe_name =VM_EXE_NAME.to_string();
let prog_name = get_foredround_window_name();
if prog_name == VM_EXE_NAME {
sta = MsgSyncState::ForceInVM;
prog_name == vm_exe_name
}
let mut state_time = time::Instant::now();
loop{
// log::trace!("current state is {:?}", sta);
match sta{
MsgSyncState::Idle => {
turn_time = 200;
// 如果当前前台的窗口是VM的窗口说明鼠标在VM窗口内切换到等待移出的逻辑
if on_vm() {
sta = MsgSyncState::FocusOnVM;
turn_time = 0;
} else {
log::trace!("foreground prog is [{}] {}, waiting: [{}] {}", prog_name, prog_name.len(), VM_EXE_NAME, VM_EXE_NAME.len());
log::trace!("Idle, waiting for mouse move to vm window");
}
},
MsgSyncState::ForceInVM => {
MsgSyncState::FocusOnVM => {
turn_time = 100;
let prog_name = get_foredround_window_name();
if prog_name != VM_EXE_NAME {
// 如果已经移出VM窗口说明从L3切换到了L0可能存在需要更新msg的情况那么就开始请求对方发送消息
if !on_vm() {
turn_time = 0;
sta = MsgSyncState::NeedSync;
//请求远端发送消息
ctx.pipe.set_update();
sta = MsgSyncState::WaitingSendMsg;
state_time = time::Instant::now();
log::trace!("waiting for remote send msg...");
}
},
MsgSyncState::NeedSync => {
MsgSyncState::WaitingSendMsg => {
turn_time = 50;
// 如果这里检测到了对方更新了文件状态,说明其已经发送完毕消息,我们开始时接收。
if ctx.pipe.get_update() {
sta = MsgSyncState::WaitingRecMsg;
turn_time = 0;
let _ = ctx.update_msg().await;
sta = MsgSyncState::None;
} else {
//最多等待1秒钟如果超过1秒钟还没收到消息那么就切换到空闲状态
if time::Instant::now() - state_time > Duration::new(1, 0) {
sta = MsgSyncState::Idle;
turn_time = 0;
}
}
},
MsgSyncState::WaitingRecMsg => {
turn_time = 0;
log::trace!("now we can recv msg");
// 接收完毕消息后切换到新的一轮判断中来
let _ = ctx.recv_msg().await;
sta = MsgSyncState::Idle;
},
}
if turn_time != 0 {
@ -184,9 +308,81 @@ async fn msg_sync(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();
let (x,y) = sys_res::cursor_pos();
let margin = 20;
if x < margin || y< margin || x > width - margin || y > height - margin {
return true
}
log::trace!("cursor pos:({},{})", x, y);
false
}
loop{
// log::trace!("current state is {:?}", sta);
match sta{
MsgSyncState::Idle => {
turn_time = 0;
// 更新下最新的文件状态,保证后续判断准确
ctx.pipe.get_update(); // refresh pipe file status
sta = MsgSyncState::FocusOnVM;
},
MsgSyncState::FocusOnVM => {
turn_time = 100;
// 当前处于窗口边缘,尝试切换到发送消息的逻辑:等待对方请求发送
if on_edge() {
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;
}
},
MsgSyncState::WaitingRecMsg => {
turn_time = 0;
sta = MsgSyncState::Idle;
// 发送消息后鼠标还是保持在边缘说明当前鼠标还是在VM窗口外面需要等待鼠标移动到VM窗口内然后再发送消息
if on_edge() {
turn_time = 100;
sta = MsgSyncState::WaitingRecMsg;
}
},
}
if turn_time != 0 {
time::sleep(Duration::new(0, turn_time*1000*1000)).await;
}
}
}
pub async fn start_msg_sync(ctx:Arc<Mutex<ClipboardSync>>) -> Result<task::JoinHandle<()>, Box<dyn Error>>{
let handle = task::spawn(msg_sync(ctx));
match ctx.lock().await.characters {
Character::Consumer => {
let handle = task::spawn(msg_reciver(ctx.clone()));
Ok(handle)
},
Character::Producer => {
let handle = task::spawn(msg_sender(ctx.clone()));
Ok(handle)
},
}
}
enum Msg <'a>{
@ -197,13 +393,14 @@ enum Msg <'a>{
}
impl ClipboardSync {
pub fn new(ip: &str, info_file:&str) -> Result<ClipboardSync, Box<dyn Error>> {
let cfg = Config::new(info_file)?;
pub fn new(ip: &str, info_file:&str, c:&Character) -> Result<ClipboardSync, Box<dyn Error>> {
Ok(ClipboardSync {
web: Client::new(),
ip: ip.to_string(),
clip: wclip::Wclip::new(),
cfg,
cfg: Config::new(info_file)?,
pipe: MsgSyncPipe::new(c)?,
characters: c.clone(),
})
}
@ -306,7 +503,7 @@ impl ClipboardSync {
Ok(resp)
}
pub async fn update_msg(&mut self) -> Result<(), Box<dyn Error>> {
pub async fn recv_msg(&mut self) -> Result<(), Box<dyn Error>> {
let resp = self.get_newest_msg().await?;
self.msg2clip(&resp).await;
Ok(())
@ -394,7 +591,7 @@ impl ClipboardSync {
}
}
pub async fn send_msg(&mut self, msg: &str) -> Result<(), Box<dyn Error>> {
async fn send_msg(&mut self, msg: &str) -> Result<(), Box<dyn Error>> {
let url = format!("http://{}/SSWeb/rd/copy_paste.jsp?send=y", self.ip);
let mut headers = HeaderMap::new();
headers.insert("Host", "ssweb".parse().unwrap());
@ -418,7 +615,7 @@ impl ClipboardSync {
Ok(())
}
pub async fn split_msg(&mut self, msg: &str) -> Result<Vec<String>, i32> {
async fn split_msg(&mut self, msg: &str) -> Result<Vec<String>, i32> {
let mut start = 0;
let mut msgs = Vec::new();
let mut cur = 0;
@ -473,7 +670,10 @@ impl ClipboardSync {
Ok(msgs) => msgs,
Err(_) => return,
},
None => return,
None => {
log::info!("clipboard is not string, ignore it");
return
},
};
//遍历所有分包并进行发送
for item in msg {
@ -487,7 +687,7 @@ async fn test_message2clip()
{
use std::fs::File;
use std::io::Read;
let mut ins = ClipboardSync::new("127.0.0.1", "info.json").unwrap();
let mut ins = ClipboardSync::new("127.0.0.1", "info.json", &Character::Producer).unwrap();
let mut html = String::new();
File::open("message.html").unwrap().read_to_string(&mut html).unwrap();
ins.msg2clip(&html).await;
@ -496,7 +696,7 @@ async fn test_message2clip()
#[tokio::test]
async fn test_start_msg_sync()
{
let ins = ClipboardSync::new("127.0.0.1:5000", "info.json").unwrap();
let ins = ClipboardSync::new("127.0.0.1:5000", "info.json", &Character::Producer).unwrap();
let ctx = Arc::new(Mutex::new(ins));
let handle = start_msg_sync(ctx.clone()).await.unwrap();
time::sleep(Duration::new(10, 0)).await;
@ -505,7 +705,7 @@ async fn test_start_msg_sync()
#[tokio::test]
async fn test_split_msg() {
let mut ins = ClipboardSync::new("127.0.0.1:5000", "info.json").unwrap();
let mut ins = ClipboardSync::new("127.0.0.1:5000", "info.json", &Character::Producer).unwrap();
let raw_msg = "在这个快速发展的世界中technology is evolving at an unprecedented rate! 我们需要不断学习和适应新的变化。#创新@未来$梦想%实现^挑战&机遇*合作+成长=成功~体验|分享<知识>探索{可能性}。Let's work together to create a better tomorrow! 生活充满了各种可能性我们要勇敢面对每一个挑战。1234567890!@#$%^&*()_+-=<>?[]{},.。;:‘’“”()【】《》——";
let msgs = ins.split_msg(raw_msg).await.unwrap();
println!("{:?}", msgs);
@ -513,7 +713,7 @@ async fn test_split_msg() {
#[tokio::test]
async fn test_send_send_msg() {
let mut ins = ClipboardSync::new("127.0.0.1:5000", "info.json").unwrap();
let mut ins = ClipboardSync::new("127.0.0.1:5000", "info.json", &Character::Producer).unwrap();
let raw_msg = "在这个快速发展的世界中technology is evolving at an unprecedented rate! 我们需要不断学习和适应新的变化。#创新@未来$梦想%实现^挑战&机遇*合作+成长=成功~体验|分享<知识>探索{可能性}。Let's work together to create a better tomorrow! 生活充满了各种可能性我们要勇敢面对每一个挑战。1234567890!@#$%^&*()_+-=<>?[]{},.。;:‘’“”()【】《》——";
ins.clip.set(&raw_msg);
ins.clip2msg().await;

View File

@ -1,21 +1,16 @@
extern crate log;
use clipboard_sync::ClipboardSync;
use std::net::IpAddr;
use trust_dns_resolver::{
config::{ResolverConfig, ResolverOpts},
Resolver,
};
use tokio::sync::{Mutex};
use clipboard_sync::{ClipboardSync, Character};
// use std::net::IpAddr;
// use trust_dns_resolver::{
// config::{ResolverConfig, ResolverOpts},
// Resolver,
// };
use tokio::sync::Mutex;
use std::sync::Arc;
use clap::{App, Arg, ArgGroup};
use clap::{App, Arg};
// const SERVER:&str = "172.19.36.79";
const SERVER:&str = "127.0.0.1:5000";
enum Character {
Producer,
Consumer,
}
const SERVER:&str = "192.168.0.116:5000";
fn param_parser() -> (Character, String) {
let mut character = Character::Consumer;
@ -61,11 +56,11 @@ fn param_parser() -> (Character, String) {
(character, info_file.to_string())
}
async fn producer(info: &str) {
let mut ins: ClipboardSync = match ClipboardSync::new(SERVER, &info) {
async fn start(info: &str, c:&Character) {
let mut ins: ClipboardSync = match ClipboardSync::new(SERVER, &info, &c) {
Ok(ins) => ins,
Err(e) => {
log::error!("create syns instance fail: {}", e);
log::error!("create clipboard sync instance fail: {}", e);
return;
}
};
@ -81,10 +76,6 @@ async fn producer(info: &str) {
let _ = handle.await;
}
async fn consumer(_info: &str) {
log::warn!("TODO: support Producer in the feature")
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
@ -107,13 +98,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// }
// }
let (character, info) = param_parser();
match character{
Character::Producer => {
producer(&info).await;
}
Character::Consumer => {
consumer(&info).await;
}
}
start(&info, &character).await;
Ok(())
}

View File

@ -46,7 +46,7 @@ pub fn get_foredround_window_rect() -> Option<(i32, i32, i32, i32)> {
}
pub fn get_foredround_window_name() -> String {
let mut name = String::new();
let full_path;
unsafe {
let foreground_window = GetForegroundWindow();
@ -71,11 +71,11 @@ pub fn get_foredround_window_name() -> String {
// let mut class_name: [u16; 256] = [0; 256];
// GetClassNameW(foreground_window, class_name.as_mut_ptr(), 256);
name = String::from_utf16_lossy(&image_name);
full_path = String::from_utf16_lossy(&image_name);
// let mut class_name_str = OsString::from_wide(&class_name);
// log::info!("Class Name: {}", class_name_str.to_string_lossy().to_string());
}
let name = name.split('\\').last().unwrap().trim_end_matches('\0').to_string();
let name = full_path.split('\\').last().unwrap().trim_end_matches('\0').to_string();
//let name = path.file_name().unwrap().to_string_lossy().to_string();
// println!("Current Proc Name: [{}]", name);
name
@ -93,7 +93,7 @@ fn test_screen_size() {
#[test]
fn test_get_foredround_window_rect() {
// env_logger::init();
let (x, y, width, height) = get_foredround_window_rect().unwrap();
let (_x, _y, width, height) = get_foredround_window_rect().unwrap();
// assert!(x >= 0);
// assert!(y >= 0);
assert!(width > 0);