feat: remote printer (#11231)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2025-03-27 15:34:27 +08:00
committed by GitHub
parent 1cb53c1f7a
commit f4bbf82363
101 changed files with 3707 additions and 211 deletions

View File

@@ -7,6 +7,14 @@ pub trait FileManager: Interface {
fs::get_home_as_string()
}
fn get_next_job_id(&self) -> i32 {
fs::get_next_job_id()
}
fn update_next_job_id(&self, id: i32) {
fs::update_next_job_id(id);
}
#[cfg(not(any(
target_os = "android",
target_os = "ios",
@@ -98,6 +106,7 @@ pub trait FileManager: Interface {
fn send_files(
&self,
id: i32,
r#type: i32,
path: String,
to: String,
file_num: i32,
@@ -106,6 +115,7 @@ pub trait FileManager: Interface {
) {
self.send(Data::SendFiles((
id,
r#type.into(),
path,
to,
file_num,
@@ -117,6 +127,7 @@ pub trait FileManager: Interface {
fn add_job(
&self,
id: i32,
r#type: i32,
path: String,
to: String,
file_num: i32,
@@ -125,6 +136,7 @@ pub trait FileManager: Interface {
) {
self.send(Data::AddJob((
id,
r#type.into(),
path,
to,
file_num,

View File

@@ -46,6 +46,7 @@ use std::{
collections::HashMap,
ffi::c_void,
num::NonZeroI64,
path::PathBuf,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, RwLock,
@@ -549,13 +550,20 @@ impl<T: InvokeUiSession> Remote<T> {
}
allow_err!(peer.send(&msg).await);
}
Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => {
Data::SendFiles((id, r#type, path, to, file_num, include_hidden, is_remote)) => {
log::info!("send files, is remote {}", is_remote);
let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version);
if is_remote {
log::debug!("New job {}, write to {} from remote {}", id, to, path);
let to = match r#type {
fs::JobType::Generic => fs::DataSource::FilePath(PathBuf::from(&to)),
fs::JobType::Printer => {
fs::DataSource::MemoryCursor(std::io::Cursor::new(Vec::new()))
}
};
self.write_jobs.push(fs::TransferJob::new_write(
id,
r#type,
path.clone(),
to,
file_num,
@@ -565,14 +573,15 @@ impl<T: InvokeUiSession> Remote<T> {
od,
));
allow_err!(
peer.send(&fs::new_send(id, path, file_num, include_hidden))
peer.send(&fs::new_send(id, r#type, path, file_num, include_hidden))
.await
);
} else {
match fs::TransferJob::new_read(
id,
r#type,
to.clone(),
path.clone(),
fs::DataSource::FilePath(PathBuf::from(&path)),
file_num,
include_hidden,
is_remote,
@@ -616,7 +625,7 @@ impl<T: InvokeUiSession> Remote<T> {
}
}
}
Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => {
Data::AddJob((id, r#type, path, to, file_num, include_hidden, is_remote)) => {
let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version);
if is_remote {
log::debug!(
@@ -627,8 +636,9 @@ impl<T: InvokeUiSession> Remote<T> {
);
let mut job = fs::TransferJob::new_write(
id,
r#type,
path.clone(),
to,
fs::DataSource::FilePath(PathBuf::from(&to)),
file_num,
include_hidden,
is_remote,
@@ -640,8 +650,9 @@ impl<T: InvokeUiSession> Remote<T> {
} else {
match fs::TransferJob::new_read(
id,
r#type,
to.clone(),
path.clone(),
fs::DataSource::FilePath(PathBuf::from(&path)),
file_num,
include_hidden,
is_remote,
@@ -679,6 +690,7 @@ impl<T: InvokeUiSession> Remote<T> {
allow_err!(
peer.send(&fs::new_send(
id,
fs::JobType::Generic,
job.remote.clone(),
job.file_num,
job.show_hidden
@@ -688,17 +700,25 @@ impl<T: InvokeUiSession> Remote<T> {
}
} else {
if let Some(job) = get_job(id, &mut self.read_jobs) {
job.is_last_job = false;
allow_err!(
peer.send(&fs::new_receive(
id,
job.path.to_string_lossy().to_string(),
job.file_num,
job.files.clone(),
job.total_size(),
))
.await
);
match &job.data_source {
fs::DataSource::FilePath(p) => {
job.is_last_job = false;
allow_err!(
peer.send(&fs::new_receive(
id,
p.to_string_lossy().to_string(),
job.file_num,
job.files.clone(),
job.total_size(),
))
.await
);
}
fs::DataSource::MemoryCursor(_) => {
// unreachable!()
log::error!("Resume job with memory cursor");
}
}
}
}
}
@@ -803,11 +823,10 @@ impl<T: InvokeUiSession> Remote<T> {
});
msg_out.set_file_action(file_action);
allow_err!(peer.send(&msg_out).await);
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
if let Some(job) = fs::remove_job(id, &mut self.write_jobs) {
job.remove_download_file();
fs::remove_job(id, &mut self.write_jobs);
}
fs::remove_job(id, &mut self.read_jobs);
let _ = fs::remove_job(id, &mut self.read_jobs);
self.remove_jobs.remove(&id);
}
Data::RemoveDir((id, path)) => {
@@ -1402,92 +1421,105 @@ impl<T: InvokeUiSession> Remote<T> {
if digest.is_upload {
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
if let Some(file) = job.files().get(digest.file_num as usize) {
let read_path = get_string(&job.join(&file.name));
let overwrite_strategy = job.default_overwrite_strategy();
if let Some(overwrite) = overwrite_strategy {
let req = FileTransferSendConfirmRequest {
id: digest.id,
file_num: digest.file_num,
union: Some(if overwrite {
file_transfer_send_confirm_request::Union::OffsetBlk(0)
} else {
file_transfer_send_confirm_request::Union::Skip(
true,
)
}),
..Default::default()
};
job.confirm(&req);
let msg = new_send_confirm(req);
allow_err!(peer.send(&msg).await);
} else {
self.handler.override_file_confirm(
digest.id,
digest.file_num,
read_path,
true,
digest.is_identical,
);
if let fs::DataSource::FilePath(p) = &job.data_source {
let read_path =
get_string(&fs::TransferJob::join(p, &file.name));
let overwrite_strategy =
job.default_overwrite_strategy();
if let Some(overwrite) = overwrite_strategy {
let req = FileTransferSendConfirmRequest {
id: digest.id,
file_num: digest.file_num,
union: Some(if overwrite {
file_transfer_send_confirm_request::Union::OffsetBlk(0)
} else {
file_transfer_send_confirm_request::Union::Skip(
true,
)
}),
..Default::default()
};
job.confirm(&req);
let msg = new_send_confirm(req);
allow_err!(peer.send(&msg).await);
} else {
self.handler.override_file_confirm(
digest.id,
digest.file_num,
read_path,
true,
digest.is_identical,
);
}
}
}
}
} else {
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
if let Some(file) = job.files().get(digest.file_num as usize) {
let write_path = get_string(&job.join(&file.name));
let overwrite_strategy = job.default_overwrite_strategy();
match fs::is_write_need_confirmation(&write_path, &digest) {
Ok(res) => match res {
DigestCheckResult::IsSame => {
let req = FileTransferSendConfirmRequest {
if let fs::DataSource::FilePath(p) = &job.data_source {
let write_path =
get_string(&fs::TransferJob::join(p, &file.name));
let overwrite_strategy =
job.default_overwrite_strategy();
match fs::is_write_need_confirmation(
&write_path,
&digest,
) {
Ok(res) => match res {
DigestCheckResult::IsSame => {
let req = FileTransferSendConfirmRequest {
id: digest.id,
file_num: digest.file_num,
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
..Default::default()
};
job.confirm(&req);
let msg = new_send_confirm(req);
allow_err!(peer.send(&msg).await);
}
DigestCheckResult::NeedConfirm(digest) => {
if let Some(overwrite) = overwrite_strategy {
let req = FileTransferSendConfirmRequest {
id: digest.id,
file_num: digest.file_num,
union: Some(if overwrite {
file_transfer_send_confirm_request::Union::OffsetBlk(0)
} else {
file_transfer_send_confirm_request::Union::Skip(true)
}),
..Default::default()
};
job.confirm(&req);
let msg = new_send_confirm(req);
allow_err!(peer.send(&msg).await);
} else {
self.handler.override_file_confirm(
digest.id,
digest.file_num,
write_path,
false,
digest.is_identical,
);
}
}
DigestCheckResult::NoSuchFile => {
let req = FileTransferSendConfirmRequest {
DigestCheckResult::NeedConfirm(digest) => {
if let Some(overwrite) = overwrite_strategy
{
let req =
FileTransferSendConfirmRequest {
id: digest.id,
file_num: digest.file_num,
union: Some(if overwrite {
file_transfer_send_confirm_request::Union::OffsetBlk(0)
} else {
file_transfer_send_confirm_request::Union::Skip(true)
}),
..Default::default()
};
job.confirm(&req);
let msg = new_send_confirm(req);
allow_err!(peer.send(&msg).await);
} else {
self.handler.override_file_confirm(
digest.id,
digest.file_num,
write_path,
false,
digest.is_identical,
);
}
}
DigestCheckResult::NoSuchFile => {
let req = FileTransferSendConfirmRequest {
id: digest.id,
file_num: digest.file_num,
union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)),
..Default::default()
};
job.confirm(&req);
let msg = new_send_confirm(req);
allow_err!(peer.send(&msg).await);
job.confirm(&req);
let msg = new_send_confirm(req);
allow_err!(peer.send(&msg).await);
}
},
Err(err) => {
println!("error receiving digest: {}", err);
}
},
Err(err) => {
println!("error receiving digest: {}", err);
}
}
}
@@ -1499,23 +1531,56 @@ impl<T: InvokeUiSession> Remote<T> {
if let Err(_err) = job.write(block).await {
// to-do: add "skip" for writing job
}
self.update_jobs_status();
if job.r#type == fs::JobType::Generic {
self.update_jobs_status();
}
}
}
Some(file_response::Union::Done(d)) => {
let mut err: Option<String> = None;
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
let mut job_type = fs::JobType::Generic;
let mut printer_data = None;
if let Some(job) = fs::remove_job(d.id, &mut self.write_jobs) {
job.modify_time();
err = job.job_error();
fs::remove_job(d.id, &mut self.write_jobs);
job_type = job.r#type;
printer_data = job.get_buf_data();
}
match job_type {
fs::JobType::Generic => {
self.handle_job_status(d.id, d.file_num, err);
}
fs::JobType::Printer =>
{
#[cfg(target_os = "windows")]
if let Some(data) = printer_data {
let printer_name = self
.handler
.printer_names
.write()
.unwrap()
.remove(&d.id);
crate::platform::send_raw_data_to_printer(
printer_name,
data,
)
.ok();
}
}
}
self.handle_job_status(d.id, d.file_num, err);
}
Some(file_response::Union::Error(e)) => {
if let Some(_job) = fs::get_job(e.id, &mut self.write_jobs) {
fs::remove_job(e.id, &mut self.write_jobs);
let job_type = fs::remove_job(e.id, &mut self.write_jobs)
.map(|j| j.r#type)
.unwrap_or(fs::JobType::Generic);
match job_type {
fs::JobType::Generic => {
self.handle_job_status(e.id, e.file_num, Some(e.error));
}
fs::JobType::Printer => {
log::error!("Printer job error: {}", e.error);
}
}
self.handle_job_status(e.id, e.file_num, Some(e.error));
}
_ => {}
}
@@ -1739,6 +1804,41 @@ impl<T: InvokeUiSession> Remote<T> {
}
}
Some(message::Union::FileAction(action)) => match action.union {
Some(file_action::Union::Send(_s)) => match _s.file_type.enum_value() {
#[cfg(target_os = "windows")]
Ok(file_transfer_send_request::FileType::Printer) => {
#[cfg(feature = "flutter")]
let action = LocalConfig::get_option(
config::keys::OPTION_PRINTER_INCOMING_JOB_ACTION,
);
#[cfg(not(feature = "flutter"))]
let action = "";
if action == "dismiss" {
// Just ignore the incoming print job.
} else {
let id = fs::get_next_job_id();
#[cfg(feature = "flutter")]
let allow_auto_print = LocalConfig::get_bool_option(
config::keys::OPTION_PRINTER_ALLOW_AUTO_PRINT,
);
#[cfg(not(feature = "flutter"))]
let allow_auto_print = false;
if allow_auto_print {
let printer_name = if action == "" {
"".to_string()
} else {
LocalConfig::get_option(
config::keys::OPTION_PRINTER_SELECTED_NAME,
)
};
self.handler.printer_response(id, _s.path, printer_name);
} else {
self.handler.printer_request(id, _s.path);
}
}
}
_ => {}
},
Some(file_action::Union::SendConfirm(c)) => {
if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) {
job.confirm(&c);
@@ -2004,7 +2104,7 @@ impl<T: InvokeUiSession> Remote<T> {
async fn handle_cliprdr_msg(
&mut self,
clip: hbb_common::message_proto::Cliprdr,
peer: &mut Stream,
_peer: &mut Stream,
) {
log::debug!("handling cliprdr msg from server peer");
#[cfg(feature = "flutter")]
@@ -2074,7 +2174,7 @@ impl<T: InvokeUiSession> Remote<T> {
}
if let Some(msg) = out_msg {
allow_err!(peer.send(&msg).await);
allow_err!(_peer.send(&msg).await);
}
}
}