windows获取窗口相关的api搞定等待完善message的管理管理逻辑。

1. message不能重复设置多次,相同的message只设置一次
2. 鼠标在L3移出去就发送剪切板消息不行,需要换成监听windows剪切板,只有内容变动的时候并且鼠标移动到窗口外面再发送,避免发送重复的消息。
3. 鼠标在L0只要前台窗口时VM horizontal时移出窗口外就进行剪切板的同步。
4. 单个消息使用时间作为magic str,这部分需要适配一下。
This commit is contained in:
Ekko.bao 2024-08-19 08:35:11 +08:00
parent 041a1526d6
commit 4d04dc2677
3 changed files with 72 additions and 52 deletions

View File

@ -16,7 +16,7 @@ select = "0.5"
scraper = "0.12" scraper = "0.12"
arboard = "3.4.0" arboard = "3.4.0"
winapi = { version = "0.3.9", features = ["winuser"] } winapi = { version = "0.3.9", features = ["winuser", "psapi"] }
[profile.test] [profile.test]
env = { "RUST_LOG" = "debug" } env = { "RUST_LOG" = "debug" }

View File

@ -1,19 +1,20 @@
use percent_encoding::{percent_encode, NON_ALPHANUMERIC}; use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use reqwest::header::{HeaderMap, CONTENT_TYPE, COOKIE}; use reqwest::header::HeaderMap;
use reqwest::{Client, Response}; use reqwest::{Client, Response};
use scraper::{Html, Selector}; use scraper::{Html, Selector};
use std::error::Error; use std::error::Error;
use std::io; use std::io;
use std::ops::Index;
use std::time::Duration; use std::time::Duration;
use tokio::sync::{Mutex}; use tokio::sync::Mutex;
use std::sync::Arc; use std::sync::Arc;
use tokio::{task, time}; use tokio::{task, time};
use std::collections::{BTreeMap, VecDeque}; use std::collections::BTreeMap;
extern crate log; extern crate log;
pub mod wclip; pub mod wclip;
pub mod sys_res; pub mod sys_res;
use sys_res::*;
pub struct ClipboardSync { pub struct ClipboardSync {
user_name: String, user_name: String,
password: String, password: String,
@ -22,7 +23,7 @@ pub struct ClipboardSync {
cookies: String, cookies: String,
clip: wclip::Wclip, clip: wclip::Wclip,
//last message magic //last message magic
cache_magic: u8, cache_magic: 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";
@ -35,11 +36,15 @@ pub struct ClipboardMsgs {
pub msgs: Vec<String>, pub msgs: Vec<String>,
} }
// 同步获取消息的线程
async fn msg_sync(ctx:Arc<Mutex<ClipboardSync>>){ async fn msg_sync(ctx:Arc<Mutex<ClipboardSync>>){
let mut ctx = ctx.lock().await; let mut ctx = ctx.lock().await;
loop{ loop{
let _ = ctx.update_msg().await; let _ = ctx.update_msg().await;
time::sleep(Duration::new(10, 0)).await; time::sleep(Duration::new(10, 0)).await;
let prog_name = get_foredround_window_name();
if prog_name == "VMware Horizon Client"{
}
} }
} }
pub async fn start_msg_sync(ctx:Arc<Mutex<ClipboardSync>>) -> Result<task::JoinHandle<()>, Box<dyn Error>>{ pub async fn start_msg_sync(ctx:Arc<Mutex<ClipboardSync>>) -> Result<task::JoinHandle<()>, Box<dyn Error>>{
@ -48,8 +53,10 @@ pub async fn start_msg_sync(ctx:Arc<Mutex<ClipboardSync>>) -> Result<task::JoinH
} }
enum Msg <'a>{ enum Msg <'a>{
Msg(&'a str), // magic str, message context
Sub((u8, u8, u8, &'a str)), Msg((String, &'a str)),
//index of message pack, total message pack number, magic str, message context
Sub((u8, u8, String, &'a str)),
} }
impl ClipboardSync { impl ClipboardSync {
@ -61,7 +68,7 @@ impl ClipboardSync {
ip: ip.to_string(), ip: ip.to_string(),
cookies: "".to_string(), cookies: "".to_string(),
clip: wclip::Wclip::new(), clip: wclip::Wclip::new(),
cache_magic: 0xff, cache_magic: "0xff".to_string(),
} }
} }
@ -163,6 +170,8 @@ impl ClipboardSync {
self.msg2clip(&resp).await; self.msg2clip(&resp).await;
Ok(()) Ok(())
} }
//解析可能存在的消息数据
fn parse_msg<'a>(msg: &'a str) -> Msg { fn parse_msg<'a>(msg: &'a str) -> Msg {
//x|x|x| -> index|total|magic| //x|x|x| -> index|total|magic|
let head = &msg[0..7]; let head = &msg[0..7];
@ -177,17 +186,21 @@ impl ClipboardSync {
}).map(|x|x.as_bytes()[0] - b'!').collect::<Vec<_>>(); }).map(|x|x.as_bytes()[0] - b'!').collect::<Vec<_>>();
if check.len() != 3 { if check.len() != 3 {
return Msg::Msg(msg) //TODO: get magic
return Msg::Msg(("maigic".to_string(), msg))
} }
return Msg::Sub((check[0], check[1], check[2], body)); return Msg::Sub((check[0], check[1], check[2].to_string(), body));
} }
//将message解析并放置到剪切板上
pub async fn msg2clip(&mut self, html: &str) { pub async fn msg2clip(&mut self, html: &str) {
// log::info!("html: {}", html); // log::info!("html: {}", html);
let document = Html::parse_document(html); let document = Html::parse_document(html);
// 所有剪切板的数据都是在code这个html lable上的
let selector = Selector::parse("code").unwrap(); let selector = Selector::parse("code").unwrap();
let mut msg_map = BTreeMap::new(); let mut msg_map = BTreeMap::new();
let mut magic_ref = 0xff; let mut magic_ref = "0xff".to_string();
let mut is_first = true; let mut is_first = true;
for element in document.select(&selector).into_iter() { for element in document.select(&selector).into_iter() {
//let text = element.text().collect::<Vec<_>>().concat(); //let text = element.text().collect::<Vec<_>>().concat();
@ -195,20 +208,21 @@ impl ClipboardSync {
log::info!("Found: [{}]", text); log::info!("Found: [{}]", text);
let msg = ClipboardSync::parse_msg(text.as_str()); let msg = ClipboardSync::parse_msg(text.as_str());
match msg { match msg {
//一整个消息。直接复制到剪切板即可
Msg::Msg(msg) => { Msg::Msg(msg) => {
if magic_ref == 0xff { if magic_ref == "0xff" {
self.clip.set(&msg); self.clip.set(msg.1);
break; break;//处理完毕。直接退出
} }
}, },
Msg::Sub((index, total, magic, _body)) => { Msg::Sub((index, total, magic, body)) => {
magic_ref = if magic_ref == 0xff {magic} else {magic_ref}; if magic_ref == "0xff" {
if magic_ref != magic { // other message ignore magic_ref = magic;
if is_first { } else {
break; if magic_ref != magic { // other message ignore
is_first = false;
continue
} }
is_first = false;
continue
} }
if msg_map.len() == 0 && ( if msg_map.len() == 0 && (
self.cache_magic == magic_ref // message is repeated ignore it self.cache_magic == magic_ref // message is repeated ignore it
@ -217,7 +231,7 @@ impl ClipboardSync {
break; break;
} }
// same msg pack, append to list // same msg pack, append to list
msg_map.insert(index, text); msg_map.insert(index, body.to_string());
if msg_map.len() as u8 == total { if msg_map.len() as u8 == total {
let msg = msg_map.into_iter().map(|(_key, v)| v).collect::<String>(); let msg = msg_map.into_iter().map(|(_key, v)| v).collect::<String>();
self.cache_magic = magic_ref; // save for next msg self.cache_magic = magic_ref; // save for next msg

View File

@ -1,21 +1,19 @@
extern crate winapi; extern crate winapi;
extern crate log; extern crate log;
use std::ptr::null_mut; use std::path::Path;
use winapi::um::winuser::{ use winapi::um::winuser::{
GetCursorPos, GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN, GetCursorPos, GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN,GetWindowRect
FindWindowA, GetWindowRect
}; };
use winapi::shared::windef::{ use winapi::shared::windef::{
POINT, POINT,
RECT, RECT,
}; };
use winapi::um::winuser::{GetForegroundWindow, GetWindowTextW, GetClassNameW, GetWindowThreadProcessId}; use winapi::um::winuser::{GetForegroundWindow, GetWindowThreadProcessId};
use std::ptr; use std::ffi::OsString;
use std::ffi::{OsString, CString};
use std::os::windows::ffi::OsStringExt; use std::os::windows::ffi::OsStringExt;
use winapi::um::processthreadsapi::{OpenProcess, GetProcessImageFileNameA}; use winapi::um::processthreadsapi::OpenProcess;
use winapi::um::winnt::PROCESS_QUERY_INFORMATION; use winapi::um::winnt::PROCESS_QUERY_INFORMATION;
use winapi::um::psapi::GetProcessImageFileNameW;
pub fn screen_size() -> (i32, i32) { pub fn screen_size() -> (i32, i32) {
unsafe { unsafe {
@ -35,69 +33,77 @@ pub fn cursor_pos() -> (i32, i32) {
} }
} }
pub fn get_window_rect() -> Option<(i32, i32, i32, i32)> { pub fn get_foredround_window_rect() -> Option<(i32, i32, i32, i32)> {
unsafe { unsafe {
let h = GetForegroundWindow(); let h = GetForegroundWindow();
let mut rect: RECT = std::mem::zeroed(); let mut rect: RECT = std::mem::zeroed();
if GetWindowRect(h, &mut rect) != 0 { if GetWindowRect(h, &mut rect) != 0 {
println!("GetWindowRect: {:?}", rect);
Some((rect.left, rect.top, rect.right, rect.bottom)) Some((rect.left, rect.top, rect.right, rect.bottom))
} else { } else {
println!("cant get foregtound window rect");
None None
} }
} }
} }
pub fn get_foredround_window_name() { pub fn get_foredround_window_name() -> String {
let mut name = OsString::new();
unsafe { unsafe {
let foreground_window = GetForegroundWindow(); let foreground_window = GetForegroundWindow();
// let mut process_id = 0; let mut process_id = 0;
// GetWindowThreadProcessId(foreground_window, &mut process_id); GetWindowThreadProcessId(foreground_window, &mut process_id);
// let mut process_name: [u16; 1024] = [0; 1024]; // let mut process_name: [u16; 1024] = [0; 1024];
// GetWindowTextW(foreground_window, process_name.as_mut_ptr(), 1024); // GetWindowTextW(foreground_window, process_name.as_mut_ptr(), 1024);
let process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid); let process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, process_id);
if process_handle.is_null() { if process_handle.is_null() {
return None; panic!("Cant open foreground process with {}!", process_id)
} }
let mut image_name = [0u8; 1024]; let mut image_name = [0u16; 1024];
let mut image_name_size = image_name.len() as u32; if GetProcessImageFileNameW(process_handle, image_name.as_mut_ptr() as *mut _, image_name.len() as u32) == 0 {
if GetProcessImageFileNameA(process_handle, image_name.as_mut_ptr() as *mut _, &mut image_name_size) == 0 { panic!("Cant Get foreground prog name!")
return None;
} }
let mut class_name: [u16; 256] = [0; 256]; // let mut class_name: [u16; 256] = [0; 256];
GetClassNameW(foreground_window, class_name.as_mut_ptr(), 256); // GetClassNameW(foreground_window, class_name.as_mut_ptr(), 256);
let mut process_name_str = OsString::from_wide(&process_name); name = OsString::from_wide(&image_name);
let mut class_name_str = OsString::from_wide(&class_name); // let mut class_name_str = OsString::from_wide(&class_name);
// log::info!("Class Name: {}", class_name_str.to_string_lossy().to_string());
log::info!("Process Name: {}", process_name_str.to_string_lossy().to_string());
log::info!("Class Name: {}", class_name_str.to_string_lossy().to_string());
} }
let path = Path::new(&name);
let name = path.file_name().unwrap().to_string_lossy().to_string();
println!("Current Proc Name: [{}]", name);
name
} }
#[test] #[test]
fn test_screen_size() { fn test_screen_size() {
// env_logger::init();
let (width, height) = screen_size(); let (width, height) = screen_size();
assert!(width > 0); assert!(width > 0);
assert!(height > 0); assert!(height > 0);
} }
#[test] #[test]
fn test_get_window_rect() { fn test_get_foredround_window_rect() {
let (x, y, width, height) = get_window_rect().unwrap(); // env_logger::init();
assert!(x >= 0); let (x, y, width, height) = get_foredround_window_rect().unwrap();
assert!(y >= 0); // assert!(x >= 0);
// assert!(y >= 0);
assert!(width > 0); assert!(width > 0);
assert!(height > 0); assert!(height > 0);
} }
#[test] #[test]
fn test_cursor_pos() { fn test_cursor_pos() {
// env_logger::init();
let (x, y) = cursor_pos(); let (x, y) = cursor_pos();
assert!(x >= 0); assert!(x >= 0);
assert!(y >= 0); assert!(y >= 0);