feat: remote printer (#11231)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -49,6 +49,7 @@ use hbb_common::{
|
||||
self, Config, LocalConfig, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT,
|
||||
READ_TIMEOUT, RELAY_PORT, RENDEZVOUS_PORT, RENDEZVOUS_SERVERS,
|
||||
},
|
||||
fs::JobType,
|
||||
get_version_number, log,
|
||||
message_proto::{option_message::BoolOption, *},
|
||||
protobuf::{Message as _, MessageField},
|
||||
@@ -3297,7 +3298,7 @@ pub enum Data {
|
||||
Close,
|
||||
Login((String, String, String, bool)),
|
||||
Message(Message),
|
||||
SendFiles((i32, String, String, i32, bool, bool)),
|
||||
SendFiles((i32, JobType, String, String, i32, bool, bool)),
|
||||
RemoveDirAll((i32, String, bool, bool)),
|
||||
ConfirmDeleteFiles((i32, i32)),
|
||||
SetNoConfirm(i32),
|
||||
@@ -3311,7 +3312,7 @@ pub enum Data {
|
||||
ToggleClipboardFile,
|
||||
NewRDP,
|
||||
SetConfirmOverrideFile((i32, i32, bool, bool, bool)),
|
||||
AddJob((i32, String, String, i32, bool, bool)),
|
||||
AddJob((i32, JobType, String, String, i32, bool, bool)),
|
||||
ResumeJob((i32, bool)),
|
||||
RecordScreen(bool),
|
||||
ElevateDirect,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,10 @@ pub fn is_support_file_copy_paste_num(ver: i64) -> bool {
|
||||
ver >= hbb_common::get_version_number("1.3.8")
|
||||
}
|
||||
|
||||
pub fn is_support_remote_print(ver: &str) -> bool {
|
||||
hbb_common::get_version_number(ver) >= hbb_common::get_version_number("1.3.9")
|
||||
}
|
||||
|
||||
pub fn is_support_file_paste_if_macos(ver: &str) -> bool {
|
||||
hbb_common::get_version_number(ver) >= hbb_common::get_version_number("1.3.9")
|
||||
}
|
||||
|
||||
@@ -196,12 +196,11 @@ pub fn core_main() -> Option<Vec<String>> {
|
||||
if config::is_disable_installation() {
|
||||
return None;
|
||||
}
|
||||
let res = platform::install_me(
|
||||
"desktopicon startmenu",
|
||||
"".to_owned(),
|
||||
true,
|
||||
args.len() > 1,
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
let options = "desktopicon startmenu";
|
||||
#[cfg(windows)]
|
||||
let options = "desktopicon startmenu printer";
|
||||
let res = platform::install_me(options, "".to_owned(), true, args.len() > 1);
|
||||
let text = match res {
|
||||
Ok(_) => translate("Installation Successful!".to_string()),
|
||||
Err(err) => {
|
||||
|
||||
@@ -1059,6 +1059,14 @@ impl InvokeUiSession for FlutterHandler {
|
||||
fn update_record_status(&self, start: bool) {
|
||||
self.push_event("record_status", &[("start", &start.to_string())], &[]);
|
||||
}
|
||||
|
||||
fn printer_request(&self, id: i32, path: String) {
|
||||
self.push_event(
|
||||
"printer_request",
|
||||
&[("id", json!(id)), ("path", json!(path))],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl FlutterHandler {
|
||||
|
||||
@@ -624,7 +624,15 @@ pub fn session_send_files(
|
||||
_is_dir: bool,
|
||||
) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.send_files(act_id, path, to, file_num, include_hidden, is_remote);
|
||||
session.send_files(
|
||||
act_id,
|
||||
fs::JobType::Generic.into(),
|
||||
path,
|
||||
to,
|
||||
file_num,
|
||||
include_hidden,
|
||||
is_remote,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -749,7 +757,15 @@ pub fn session_add_job(
|
||||
is_remote: bool,
|
||||
) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.add_job(act_id, path, to, file_num, include_hidden, is_remote);
|
||||
session.add_job(
|
||||
act_id,
|
||||
fs::JobType::Generic.into(),
|
||||
path,
|
||||
to,
|
||||
file_num,
|
||||
include_hidden,
|
||||
is_remote,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1668,6 +1684,17 @@ pub fn session_toggle_virtual_display(session_id: SessionID, index: i32, on: boo
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_printer_response(
|
||||
session_id: SessionID,
|
||||
id: i32,
|
||||
path: String,
|
||||
printer_name: String,
|
||||
) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.printer_response(id, path, printer_name);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_set_home_dir(_home: String) {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
{
|
||||
@@ -2362,6 +2389,68 @@ pub fn main_audio_support_loopback() -> SyncReturn<bool> {
|
||||
SyncReturn(is_surpport)
|
||||
}
|
||||
|
||||
pub fn main_get_printer_names() -> SyncReturn<String> {
|
||||
#[cfg(target_os = "windows")]
|
||||
return SyncReturn(
|
||||
serde_json::to_string(&crate::platform::windows::get_printer_names().unwrap_or_default())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
return SyncReturn("".to_owned());
|
||||
}
|
||||
|
||||
pub fn main_get_common(key: String) -> String {
|
||||
if key == "is-printer-installed" {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
return match remote_printer::is_rd_printer_installed(&get_app_name()) {
|
||||
Ok(r) => r.to_string(),
|
||||
Err(e) => e.to_string(),
|
||||
};
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
return false.to_string();
|
||||
} else if key == "is-support-printer-driver" {
|
||||
#[cfg(target_os = "windows")]
|
||||
return crate::platform::is_win_10_or_greater().to_string();
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
return false.to_string();
|
||||
} else if key == "transfer-job-id" {
|
||||
return hbb_common::fs::get_next_job_id().to_string();
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_get_common_sync(key: String) -> SyncReturn<String> {
|
||||
SyncReturn(main_get_common(key))
|
||||
}
|
||||
|
||||
pub fn main_set_common(_key: String, _value: String) {
|
||||
#[cfg(target_os = "windows")]
|
||||
if _key == "install-printer" && crate::platform::is_win_10_or_greater() {
|
||||
std::thread::spawn(move || {
|
||||
let (success, msg) = match remote_printer::install_update_printer(&get_app_name()) {
|
||||
Ok(_) => (true, "".to_owned()),
|
||||
Err(e) => {
|
||||
let err = e.to_string();
|
||||
log::error!("Failed to install/update rd printer: {}", &err);
|
||||
(false, err)
|
||||
}
|
||||
};
|
||||
let data = HashMap::from([
|
||||
("name", serde_json::json!("install-printer-res")),
|
||||
("success", serde_json::json!(success)),
|
||||
("msg", serde_json::json!(msg)),
|
||||
]);
|
||||
let _res = flutter::push_global_event(
|
||||
flutter::APP_TYPE_MAIN,
|
||||
serde_json::ser::to_string(&data).unwrap_or("".to_owned()),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub mod server_side {
|
||||
use hbb_common::{config, log};
|
||||
|
||||
@@ -272,6 +272,8 @@ pub enum Data {
|
||||
HwCodecConfig(Option<String>),
|
||||
RemoveTrustedDevices(Vec<Bytes>),
|
||||
ClearTrustedDevices,
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
PrinterData(Vec<u8>),
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
@@ -461,7 +463,7 @@ async fn handle(data: Data, stream: &mut Connection) {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|x| x.1 == crate::server::AuthConnType::Remote)
|
||||
.filter(|x| x.conn_type == crate::server::AuthConnType::Remote)
|
||||
.count();
|
||||
allow_err!(stream.send(&Data::VideoConnCount(Some(n))).await);
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "没有摄像头"),
|
||||
("d3d_render_tip", "当启用 D3D 渲染时,某些机器可能无法显示远程画面。"),
|
||||
("Use D3D rendering", "使用 D3D 渲染"),
|
||||
("Printer", "打印机"),
|
||||
("printer-os-requirement-tip", "打印机的传出功能需要 Windows 10 或更高版本。"),
|
||||
("printer-requires-installed-{}-client-tip", "请先安装 {} 客户端。"),
|
||||
("printer-{}-not-installed-tip", "未安装 {} 打印机。"),
|
||||
("printer-{}-ready-tip", "{} 打印机已安装,您可以使用打印功能了。"),
|
||||
("Install {} Printer", "安装 {} 打印机"),
|
||||
("Outgoing Print Jobs", "传出的打印任务"),
|
||||
("Incomming Print Jobs", "传入的打印任务"),
|
||||
("Incoming Print Job", "传入的打印任务"),
|
||||
("use-the-default-printer-tip", "使用默认的打印机执行"),
|
||||
("use-the-selected-printer-tip", "使用选择的打印机执行"),
|
||||
("auto-print-tip", "使用选择的打印机自动执行"),
|
||||
("print-incoming-job-confirm-tip", "您收到一个远程打印任务,您想在本地执行它吗?"),
|
||||
("remote-printing-disallowed-tile-tip", "不允许远程打印"),
|
||||
("remote-printing-disallowed-text-tip", "被控端的权限设置拒绝了远程打印。"),
|
||||
("save-settings-tip", "保存设置"),
|
||||
("dont-show-again-tip", "不再显示此信息"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "Keine Kameras"),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -241,5 +241,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Please upgrade the RustDesk client to version {} or newer on the remote side!"),
|
||||
("view_camera_unsupported_tip", "The remote device does not support viewing the camera."),
|
||||
("d3d_render_tip", "When D3D rendering is enabled, the remote control screen may be black on some machines."),
|
||||
("printer-requires-installed-{}-client-tip", "In order to use remote printing, {} needs to be installed on this device."),
|
||||
("printer-os-requirement-tip", "The printer outgoing function requires Windows 10 or higher."),
|
||||
("printer-{}-not-installed-tip", "The {} Printer is not installed."),
|
||||
("printer-{}-ready-tip", "The {} Printer is installed and ready to use."),
|
||||
("auto-print-tip", "Print automatically using the selected printer."),
|
||||
("print-incoming-job-confirm-tip", "You received a print job from remote. Do you want to execute it at your side?"),
|
||||
("use-the-default-printer-tip", "Use the default printer"),
|
||||
("use-the-selected-printer-tip", "Use the selected printer"),
|
||||
("remote-printing-disallowed-tile-tip", "Remote Printing disallowed"),
|
||||
("remote-printing-disallowed-text-tip", "The permission settings of the controlled side deny Remote Printing."),
|
||||
("save-settings-tip", "Save settings"),
|
||||
("dont-show-again-tip", "Don't show this again"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "No hay cámaras"),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "Aucune caméra"),
|
||||
("d3d_render_tip", "Sur certaines machines, l’écran du contrôle à distance peut rester noir lors de l’utilisation du rendu D3D."),
|
||||
("Use D3D rendering", "Utiliser le rendu D3D"),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "Nessuna camera"),
|
||||
("d3d_render_tip", "Quando è abilitato il rendering D3D, in alcuni computer la schermata del telecomando potrebbe essere nera."),
|
||||
("Use D3D rendering", "Usa rendering D3D"),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "Nav kameru"),
|
||||
("d3d_render_tip", "Ja ir iespējota D3D renderēšana, dažās ierīcēs tālvadības pults ekrāns var būt melns."),
|
||||
("Use D3D rendering", "Izmantot D3D renderēšanu"),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "Geen camera's"),
|
||||
("d3d_render_tip", "Wanneer D3D-rendering is ingeschakeld kan het externe scherm op sommige apparaten, zwart zijn."),
|
||||
("Use D3D rendering", "Gebruik D3D-rendering"),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "Камера отсутствует"),
|
||||
("d3d_render_tip", "При включении визуализации D3D на некоторых устройствах удалённый экран может быть чёрным."),
|
||||
("Use D3D rendering", "Использовать визуализацию D3D"),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "Peruna càmera"),
|
||||
("d3d_render_tip", "Cando sa renderizatzione D3D est abilitada, s'ischermu de controllu remotu diat pòdere èssere nieddu in unas cantas màchinas"),
|
||||
("Use D3D rendering", "Imprea sa renderizatzione D3D"),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", "沒有鏡頭"),
|
||||
("d3d_render_tip", "當啟用 D3D 渲染時,某些機器可能會無法顯示遠端畫面。"),
|
||||
("Use D3D rendering", "使用 D3D 渲染"),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -664,5 +664,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
("printer-os-requirement-tip", ""),
|
||||
("printer-requires-installed-{}-client-tip", ""),
|
||||
("printer-{}-not-installed-tip", ""),
|
||||
("printer-{}-ready-tip", ""),
|
||||
("Install {} Printer", ""),
|
||||
("Outgoing Print Jobs", ""),
|
||||
("Incomming Print Jobs", ""),
|
||||
("Incoming Print Job", ""),
|
||||
("use-the-default-printer-tip", ""),
|
||||
("use-the-selected-printer-tip", ""),
|
||||
("auto-print-tip", ""),
|
||||
("print-incoming-job-confirm-tip", ""),
|
||||
("remote-printing-disallowed-tile-tip", ""),
|
||||
("remote-printing-disallowed-text-tip", ""),
|
||||
("save-settings-tip", ""),
|
||||
("dont-show-again-tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <windows.h>
|
||||
#include <wtsapi32.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <comdef.h>
|
||||
#include <xpsprint.h>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <intrin.h>
|
||||
@@ -11,6 +13,7 @@
|
||||
#include <versionhelpers.h>
|
||||
#include <vector>
|
||||
#include <sddl.h>
|
||||
#include <memory>
|
||||
|
||||
extern "C" uint32_t get_session_user_info(PWSTR bufin, uint32_t nin, uint32_t id);
|
||||
|
||||
@@ -859,4 +862,114 @@ extern "C"
|
||||
|
||||
return isRunning;
|
||||
}
|
||||
} // end of extern "C"
|
||||
} // end of extern "C"
|
||||
|
||||
// Remote printing
|
||||
extern "C"
|
||||
{
|
||||
#pragma comment(lib, "XpsPrint.lib")
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4995)
|
||||
|
||||
#define PRINT_XPS_CHECK_HR(hr, msg) \
|
||||
if (FAILED(hr)) \
|
||||
{ \
|
||||
_com_error err(hr); \
|
||||
flog("%s Error: %s\n", msg, err.ErrorMessage()); \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
int PrintXPSRawData(LPWSTR printerName, BYTE *rawData, ULONG dataSize)
|
||||
{
|
||||
BOOL isCoInitializeOk = FALSE;
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (hr == RPC_E_CHANGED_MODE)
|
||||
{
|
||||
hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
}
|
||||
if (hr == S_OK)
|
||||
{
|
||||
isCoInitializeOk = TRUE;
|
||||
}
|
||||
std::shared_ptr<int> coInitGuard(nullptr, [isCoInitializeOk](int *) {
|
||||
if (isCoInitializeOk) CoUninitialize();
|
||||
});
|
||||
|
||||
IXpsOMObjectFactory *xpsFactory = nullptr;
|
||||
hr = CoCreateInstance(
|
||||
__uuidof(XpsOMObjectFactory),
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IXpsOMObjectFactory),
|
||||
reinterpret_cast<LPVOID *>(&xpsFactory));
|
||||
PRINT_XPS_CHECK_HR(hr, "Failed to create XPS object factory.");
|
||||
std::shared_ptr<IXpsOMObjectFactory> xpsFactoryGuard(
|
||||
xpsFactory,
|
||||
[](IXpsOMObjectFactory *xpsFactory) {
|
||||
xpsFactory->Release();
|
||||
});
|
||||
|
||||
HANDLE completionEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
if (completionEvent == nullptr)
|
||||
{
|
||||
flog("Failed to create completion event. Last error: %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
std::shared_ptr<HANDLE> completionEventGuard(
|
||||
&completionEvent,
|
||||
[](HANDLE *completionEvent) {
|
||||
CloseHandle(*completionEvent);
|
||||
});
|
||||
|
||||
IXpsPrintJob *job = nullptr;
|
||||
IXpsPrintJobStream *jobStream = nullptr;
|
||||
// `StartXpsPrintJob()` is deprecated, but we still use it for compatibility.
|
||||
// We may change to use the `Print Document Package API` in the future.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/printdocs/xpsprint-functions
|
||||
hr = StartXpsPrintJob(
|
||||
printerName,
|
||||
L"Print Job 1",
|
||||
nullptr,
|
||||
nullptr,
|
||||
completionEvent,
|
||||
nullptr,
|
||||
0,
|
||||
&job,
|
||||
&jobStream,
|
||||
nullptr);
|
||||
PRINT_XPS_CHECK_HR(hr, "Failed to start XPS print job.");
|
||||
|
||||
std::shared_ptr<IXpsPrintJobStream> jobStreamGuard(jobStream, [](IXpsPrintJobStream *jobStream) {
|
||||
jobStream->Release();
|
||||
});
|
||||
BOOL jobOk = FALSE;
|
||||
std::shared_ptr<IXpsPrintJob> jobGuard(job, [&jobOk](IXpsPrintJob* job) {
|
||||
if (jobOk == FALSE)
|
||||
{
|
||||
job->Cancel();
|
||||
}
|
||||
job->Release();
|
||||
});
|
||||
|
||||
DWORD bytesWritten = 0;
|
||||
hr = jobStream->Write(rawData, dataSize, &bytesWritten);
|
||||
PRINT_XPS_CHECK_HR(hr, "Failed to write data to print job stream.");
|
||||
|
||||
hr = jobStream->Close();
|
||||
PRINT_XPS_CHECK_HR(hr, "Failed to close print job stream.");
|
||||
|
||||
// Wait about 5 minutes for the print job to complete.
|
||||
DWORD waitMillis = 300 * 1000;
|
||||
DWORD waitResult = WaitForSingleObject(completionEvent, waitMillis);
|
||||
if (waitResult != WAIT_OBJECT_0)
|
||||
{
|
||||
flog("Wait for print job completion failed. Last error: %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
jobOk = TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
@@ -21,23 +21,22 @@ use std::{
|
||||
fs,
|
||||
io::{self, prelude::*},
|
||||
mem,
|
||||
os::windows::process::CommandExt,
|
||||
os::{raw::c_ulong, windows::process::CommandExt},
|
||||
path::*,
|
||||
ptr::null_mut,
|
||||
sync::{atomic::Ordering, Arc, Mutex},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use wallpaper;
|
||||
#[cfg(not(debug_assertions))]
|
||||
use winapi::um::libloaderapi::{LoadLibraryExW, LOAD_LIBRARY_SEARCH_USER_DIRS};
|
||||
use winapi::{
|
||||
ctypes::c_void,
|
||||
shared::{minwindef::*, ntdef::NULL, windef::*, winerror::*},
|
||||
um::{
|
||||
errhandlingapi::GetLastError,
|
||||
handleapi::CloseHandle,
|
||||
libloaderapi::{
|
||||
GetProcAddress, LoadLibraryExA, LoadLibraryExW, LOAD_LIBRARY_SEARCH_SYSTEM32,
|
||||
LOAD_LIBRARY_SEARCH_USER_DIRS,
|
||||
},
|
||||
libloaderapi::{GetProcAddress, LoadLibraryExA, LOAD_LIBRARY_SEARCH_SYSTEM32},
|
||||
minwinbase::STILL_ACTIVE,
|
||||
processthreadsapi::{
|
||||
GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess,
|
||||
@@ -54,6 +53,10 @@ use winapi::{
|
||||
TOKEN_ELEVATION, TOKEN_QUERY,
|
||||
},
|
||||
winreg::HKEY_CURRENT_USER,
|
||||
winspool::{
|
||||
EnumPrintersW, GetDefaultPrinterW, PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL,
|
||||
PRINTER_INFO_1W,
|
||||
},
|
||||
winuser::*,
|
||||
},
|
||||
};
|
||||
@@ -73,6 +76,7 @@ pub const SET_FOREGROUND_WINDOW: &'static str = "SET_FOREGROUND_WINDOW";
|
||||
|
||||
const REG_NAME_INSTALL_DESKTOPSHORTCUTS: &str = "DESKTOPSHORTCUTS";
|
||||
const REG_NAME_INSTALL_STARTMENUSHORTCUTS: &str = "STARTMENUSHORTCUTS";
|
||||
const REG_NAME_INSTALL_PRINTER: &str = "PRINTER";
|
||||
|
||||
pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
|
||||
unsafe {
|
||||
@@ -1011,6 +1015,10 @@ pub fn get_install_options() -> String {
|
||||
if let Some(start_menu_shortcuts) = start_menu_shortcuts {
|
||||
opts.insert(REG_NAME_INSTALL_STARTMENUSHORTCUTS, start_menu_shortcuts);
|
||||
}
|
||||
let printer = get_reg_of_hkcr(&subkey, REG_NAME_INSTALL_PRINTER);
|
||||
if let Some(printer) = printer {
|
||||
opts.insert(REG_NAME_INSTALL_PRINTER, printer);
|
||||
}
|
||||
serde_json::to_string(&opts).unwrap_or("{}".to_owned())
|
||||
}
|
||||
|
||||
@@ -1136,6 +1144,7 @@ fn get_after_install(
|
||||
exe: &str,
|
||||
reg_value_start_menu_shortcuts: Option<String>,
|
||||
reg_value_desktop_shortcuts: Option<String>,
|
||||
reg_value_printer: Option<String>,
|
||||
) -> String {
|
||||
let app_name = crate::get_app_name();
|
||||
let ext = app_name.to_lowercase();
|
||||
@@ -1159,12 +1168,20 @@ fn get_after_install(
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let reg_printer = reg_value_printer
|
||||
.map(|v| {
|
||||
format!(
|
||||
"reg add HKEY_CLASSES_ROOT\\.{ext} /f /v {REG_NAME_INSTALL_PRINTER} /t REG_SZ /d \"{v}\""
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
format!("
|
||||
chcp 65001
|
||||
reg add HKEY_CLASSES_ROOT\\.{ext} /f
|
||||
{desktop_shortcuts}
|
||||
{start_menu_shortcuts}
|
||||
{reg_printer}
|
||||
reg add HKEY_CLASSES_ROOT\\.{ext}\\DefaultIcon /f
|
||||
reg add HKEY_CLASSES_ROOT\\.{ext}\\DefaultIcon /f /ve /t REG_SZ /d \"\\\"{exe}\\\",0\"
|
||||
reg add HKEY_CLASSES_ROOT\\.{ext}\\shell /f
|
||||
@@ -1249,6 +1266,7 @@ oLink.Save
|
||||
let tray_shortcut = get_tray_shortcut(&exe, &tmp_path)?;
|
||||
let mut reg_value_desktop_shortcuts = "0".to_owned();
|
||||
let mut reg_value_start_menu_shortcuts = "0".to_owned();
|
||||
let mut reg_value_printer = "0".to_owned();
|
||||
let mut shortcuts = Default::default();
|
||||
if options.contains("desktopicon") {
|
||||
shortcuts = format!(
|
||||
@@ -1268,6 +1286,10 @@ copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{start_menu}\\\"
|
||||
);
|
||||
reg_value_start_menu_shortcuts = "1".to_owned();
|
||||
}
|
||||
let install_printer = options.contains("printer") && crate::platform::is_win_10_or_greater();
|
||||
if install_printer {
|
||||
reg_value_printer = "1".to_owned();
|
||||
}
|
||||
|
||||
let meta = std::fs::symlink_metadata(std::env::current_exe()?)?;
|
||||
let size = meta.len() / 1024;
|
||||
@@ -1338,7 +1360,8 @@ copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\"
|
||||
after_install = get_after_install(
|
||||
&exe,
|
||||
Some(reg_value_start_menu_shortcuts),
|
||||
Some(reg_value_desktop_shortcuts)
|
||||
Some(reg_value_desktop_shortcuts),
|
||||
Some(reg_value_printer)
|
||||
),
|
||||
sleep = if debug { "timeout 300" } else { "" },
|
||||
dels = if debug { "" } else { &dels },
|
||||
@@ -1346,13 +1369,22 @@ copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\"
|
||||
import_config = get_import_config(&exe),
|
||||
);
|
||||
run_cmds(cmds, debug, "install")?;
|
||||
if install_printer {
|
||||
allow_err!(remote_printer::install_update_printer(
|
||||
&crate::get_app_name()
|
||||
));
|
||||
}
|
||||
run_after_run_cmds(silent);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_after_install() -> ResultType<()> {
|
||||
let (_, _, _, exe) = get_install_info();
|
||||
run_cmds(get_after_install(&exe, None, None), true, "after_install")
|
||||
run_cmds(
|
||||
get_after_install(&exe, None, None, None),
|
||||
true,
|
||||
"after_install",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn run_before_uninstall() -> ResultType<()> {
|
||||
@@ -1413,6 +1445,9 @@ fn get_uninstall(kill_self: bool) -> String {
|
||||
}
|
||||
|
||||
pub fn uninstall_me(kill_self: bool) -> ResultType<()> {
|
||||
if crate::platform::is_win_10_or_greater() {
|
||||
remote_printer::uninstall_printer(&crate::get_app_name());
|
||||
}
|
||||
run_cmds(get_uninstall(kill_self), true, "uninstall")
|
||||
}
|
||||
|
||||
@@ -1570,9 +1605,18 @@ pub fn bootstrap() -> bool {
|
||||
*config::EXE_RENDEZVOUS_SERVER.write().unwrap() = lic.host.clone();
|
||||
}
|
||||
|
||||
set_safe_load_dll()
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
true
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
// This function will cause `'sciter.dll' was not found neither in PATH nor near the current executable.` when debugging RustDesk.
|
||||
set_safe_load_dll()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn set_safe_load_dll() -> bool {
|
||||
if !unsafe { set_default_dll_directories() } {
|
||||
return false;
|
||||
@@ -1589,6 +1633,7 @@ fn set_safe_load_dll() -> bool {
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories
|
||||
#[cfg(not(debug_assertions))]
|
||||
unsafe fn set_default_dll_directories() -> bool {
|
||||
let module = LoadLibraryExW(
|
||||
wide_string("Kernel32.dll").as_ptr(),
|
||||
@@ -2728,3 +2773,119 @@ pub mod reg_display_settings {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_printer_names() -> ResultType<Vec<String>> {
|
||||
let mut needed_bytes = 0;
|
||||
let mut returned_count = 0;
|
||||
|
||||
unsafe {
|
||||
// First call to get required buffer size
|
||||
EnumPrintersW(
|
||||
PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
|
||||
std::ptr::null_mut(),
|
||||
1,
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
&mut needed_bytes,
|
||||
&mut returned_count,
|
||||
);
|
||||
|
||||
let mut buffer = vec![0u8; needed_bytes as usize];
|
||||
|
||||
if EnumPrintersW(
|
||||
PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
|
||||
std::ptr::null_mut(),
|
||||
1,
|
||||
buffer.as_mut_ptr() as *mut _,
|
||||
needed_bytes,
|
||||
&mut needed_bytes,
|
||||
&mut returned_count,
|
||||
) == 0
|
||||
{
|
||||
return Err(anyhow!("Failed to enumerate printers"));
|
||||
}
|
||||
|
||||
let ptr = buffer.as_ptr() as *const PRINTER_INFO_1W;
|
||||
let printers = std::slice::from_raw_parts(ptr, returned_count as usize);
|
||||
|
||||
Ok(printers
|
||||
.iter()
|
||||
.filter_map(|p| {
|
||||
let name = p.pName;
|
||||
if !name.is_null() {
|
||||
let mut len = 0;
|
||||
while len < 500 {
|
||||
if name.add(len).is_null() || *name.add(len) == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
if len > 0 && len < 500 {
|
||||
Some(String::from_utf16_lossy(std::slice::from_raw_parts(
|
||||
name, len,
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn PrintXPSRawData(printer_name: *const u16, raw_data: *const u8, data_size: c_ulong) -> DWORD;
|
||||
}
|
||||
|
||||
pub fn send_raw_data_to_printer(printer_name: Option<String>, data: Vec<u8>) -> ResultType<()> {
|
||||
let mut printer_name = printer_name.unwrap_or_default();
|
||||
if printer_name.is_empty() {
|
||||
// use GetDefaultPrinter to get the default printer name
|
||||
let mut needed_bytes = 0;
|
||||
unsafe {
|
||||
GetDefaultPrinterW(std::ptr::null_mut(), &mut needed_bytes);
|
||||
}
|
||||
if needed_bytes > 0 {
|
||||
let mut default_printer_name = vec![0u16; needed_bytes as usize];
|
||||
unsafe {
|
||||
GetDefaultPrinterW(
|
||||
default_printer_name.as_mut_ptr() as *mut _,
|
||||
&mut needed_bytes,
|
||||
);
|
||||
}
|
||||
printer_name = String::from_utf16_lossy(&default_printer_name[..needed_bytes as usize]);
|
||||
}
|
||||
} else {
|
||||
if let Ok(names) = crate::platform::windows::get_printer_names() {
|
||||
if !names.contains(&printer_name) {
|
||||
// Don't set the first printer as current printer.
|
||||
// It may not be the desired printer.
|
||||
log::error!(
|
||||
"Printer name \"{}\" not found, ignore the print job",
|
||||
printer_name
|
||||
);
|
||||
bail!("Printer name \"{}\" not found", &printer_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if printer_name.is_empty() {
|
||||
log::error!("Failed to get printer name");
|
||||
return Err(anyhow!("Failed to get printer name"));
|
||||
}
|
||||
|
||||
let printer_name = wide_string(&printer_name);
|
||||
unsafe {
|
||||
let res = PrintXPSRawData(
|
||||
printer_name.as_ptr(),
|
||||
data.as_ptr() as *const u8,
|
||||
data.len() as c_ulong,
|
||||
);
|
||||
if res != 0 {
|
||||
bail!("Failed to send file to printer, see logs in C:\\Windows\\temp\\test_rustdesk.log for more details.");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@ mod service;
|
||||
mod video_qos;
|
||||
pub mod video_service;
|
||||
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
pub mod printer_service;
|
||||
|
||||
pub type Childs = Arc<Mutex<Vec<std::process::Child>>>;
|
||||
type ConnMap = HashMap<i32, ConnInner>;
|
||||
|
||||
@@ -129,6 +132,20 @@ pub fn new() -> ServerPtr {
|
||||
server.add_service(Box::new(input_service::new_window_focus()));
|
||||
}
|
||||
}
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
{
|
||||
match printer_service::init(&crate::get_app_name()) {
|
||||
Ok(()) => {
|
||||
log::info!("printer service initialized");
|
||||
server.add_service(Box::new(printer_service::new(
|
||||
printer_service::NAME.to_owned(),
|
||||
)));
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("printer service init failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Arc::new(RwLock::new(server))
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ use hbb_common::platform::linux::run_cmds;
|
||||
use hbb_common::protobuf::EnumOrUnknown;
|
||||
use hbb_common::{
|
||||
config::{self, keys, Config, TrustedDevice},
|
||||
fs::{self, can_enable_overwrite_detection},
|
||||
fs::{self, can_enable_overwrite_detection, JobType},
|
||||
futures::{SinkExt, StreamExt},
|
||||
get_time, get_version_number,
|
||||
message_proto::{option_message::BoolOption, permission_info::Permission},
|
||||
@@ -67,7 +67,7 @@ lazy_static::lazy_static! {
|
||||
static ref LOGIN_FAILURES: [Arc::<Mutex<HashMap<String, (i32, i32, i32)>>>; 2] = Default::default();
|
||||
static ref SESSIONS: Arc::<Mutex<HashMap<SessionKey, Session>>> = Default::default();
|
||||
static ref ALIVE_CONNS: Arc::<Mutex<Vec<i32>>> = Default::default();
|
||||
pub static ref AUTHED_CONNS: Arc::<Mutex<Vec<(i32, AuthConnType, SessionKey)>>> = Default::default();
|
||||
pub static ref AUTHED_CONNS: Arc::<Mutex<Vec<AuthedConn>>> = Default::default();
|
||||
static ref SWITCH_SIDES_UUID: Arc::<Mutex<HashMap<String, (Instant, uuid::Uuid)>>> = Default::default();
|
||||
static ref WAKELOCK_SENDER: Arc::<Mutex<std::sync::mpsc::Sender<(usize, usize)>>> = Arc::new(Mutex::new(start_wakelock_thread()));
|
||||
}
|
||||
@@ -245,6 +245,8 @@ pub struct Connection {
|
||||
follow_remote_cursor: bool,
|
||||
follow_remote_window: bool,
|
||||
multi_ui_session: bool,
|
||||
tx_from_authed: mpsc::UnboundedSender<ipc::Data>,
|
||||
printer_data: Vec<(Instant, String, Vec<u8>)>,
|
||||
}
|
||||
|
||||
impl ConnInner {
|
||||
@@ -309,6 +311,7 @@ impl Connection {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<(Instant, Arc<Message>)>();
|
||||
let (tx_video, mut rx_video) = mpsc::unbounded_channel::<(Instant, Arc<Message>)>();
|
||||
let (tx_input, _rx_input) = std_mpsc::channel();
|
||||
let (tx_from_authed, mut rx_from_authed) = mpsc::unbounded_channel::<ipc::Data>();
|
||||
let mut hbbs_rx = crate::hbbs_http::sync::signal_receiver();
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let (tx_cm_stream_ready, _rx_cm_stream_ready) = mpsc::channel(1);
|
||||
@@ -396,6 +399,8 @@ impl Connection {
|
||||
delayed_read_dir: None,
|
||||
#[cfg(target_os = "macos")]
|
||||
retina: Retina::default(),
|
||||
tx_from_authed,
|
||||
printer_data: Vec::new(),
|
||||
};
|
||||
let addr = hbb_common::try_into_v4(addr);
|
||||
if !conn.on_open(addr).await {
|
||||
@@ -758,6 +763,19 @@ impl Connection {
|
||||
break;
|
||||
}
|
||||
},
|
||||
Some(data) = rx_from_authed.recv() => {
|
||||
match data {
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
ipc::Data::PrinterData(data) => {
|
||||
if config::Config::get_bool_option(config::keys::OPTION_ENABLE_REMOTE_PRINTER) {
|
||||
conn.send_printer_request(data).await;
|
||||
} else {
|
||||
conn.send_remote_printing_disallowed().await;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ = second_timer.tick() => {
|
||||
#[cfg(windows)]
|
||||
conn.portable_check();
|
||||
@@ -1104,6 +1122,22 @@ impl Connection {
|
||||
});
|
||||
}
|
||||
|
||||
fn get_files_for_audit(job_type: fs::JobType, mut files: Vec<FileEntry>) -> Vec<(String, i64)> {
|
||||
files
|
||||
.drain(..)
|
||||
.map(|f| {
|
||||
(
|
||||
if job_type == fs::JobType::Printer {
|
||||
"Remote print".to_owned()
|
||||
} else {
|
||||
f.name
|
||||
},
|
||||
f.size as _,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn post_file_audit(
|
||||
&self,
|
||||
r#type: FileAuditType,
|
||||
@@ -1212,6 +1246,8 @@ impl Connection {
|
||||
self.inner.id(),
|
||||
auth_conn_type,
|
||||
self.session_key(),
|
||||
self.tx_from_authed.clone(),
|
||||
self.lr.clone(),
|
||||
));
|
||||
self.session_last_recv_time = SESSIONS
|
||||
.lock()
|
||||
@@ -2318,7 +2354,15 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
Some(message::Union::FileAction(fa)) => {
|
||||
if self.file_transfer.is_some() {
|
||||
let mut handle_fa = self.file_transfer.is_some();
|
||||
if !handle_fa {
|
||||
if let Some(file_action::Union::Send(s)) = fa.union.as_ref() {
|
||||
if JobType::from_proto(s.file_type) == JobType::Printer {
|
||||
handle_fa = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if handle_fa {
|
||||
if self.delayed_read_dir.is_some() {
|
||||
if let Some(file_action::Union::ReadDir(rd)) = fa.union {
|
||||
self.delayed_read_dir = Some((rd.path, rd.include_hidden));
|
||||
@@ -2375,10 +2419,32 @@ impl Connection {
|
||||
&self.lr.version,
|
||||
));
|
||||
let path = s.path.clone();
|
||||
let r#type = JobType::from_proto(s.file_type);
|
||||
let data_source;
|
||||
match r#type {
|
||||
JobType::Generic => {
|
||||
data_source =
|
||||
fs::DataSource::FilePath(PathBuf::from(&path));
|
||||
}
|
||||
JobType::Printer => {
|
||||
if let Some(pd) =
|
||||
self.printer_data.iter().find(|(_, p, _)| *p == path)
|
||||
{
|
||||
data_source = fs::DataSource::MemoryCursor(
|
||||
std::io::Cursor::new(pd.2.clone()),
|
||||
);
|
||||
self.printer_data.retain(|f| f.1 != path);
|
||||
} else {
|
||||
// Ignore this message if the printer data is not found
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
match fs::TransferJob::new_read(
|
||||
id,
|
||||
r#type,
|
||||
"".to_string(),
|
||||
path.clone(),
|
||||
data_source,
|
||||
s.file_num,
|
||||
s.include_hidden,
|
||||
false,
|
||||
@@ -2390,19 +2456,21 @@ impl Connection {
|
||||
Ok(mut job) => {
|
||||
self.send(fs::new_dir(id, path, job.files().to_vec()))
|
||||
.await;
|
||||
let mut files = job.files().to_owned();
|
||||
let files = job.files().to_owned();
|
||||
job.is_remote = true;
|
||||
job.conn_id = self.inner.id();
|
||||
let job_type = job.r#type;
|
||||
self.read_jobs.push(job);
|
||||
self.file_timer =
|
||||
crate::rustdesk_interval(time::interval(MILLI1));
|
||||
self.post_file_audit(
|
||||
FileAuditType::RemoteSend,
|
||||
&s.path,
|
||||
files
|
||||
.drain(..)
|
||||
.map(|f| (f.name, f.size as _))
|
||||
.collect(),
|
||||
if job_type == fs::JobType::Printer {
|
||||
"Remote print"
|
||||
} else {
|
||||
&s.path
|
||||
},
|
||||
Self::get_files_for_audit(job_type, files),
|
||||
json!({}),
|
||||
);
|
||||
}
|
||||
@@ -2433,11 +2501,7 @@ impl Connection {
|
||||
self.post_file_audit(
|
||||
FileAuditType::RemoteReceive,
|
||||
&r.path,
|
||||
r.files
|
||||
.to_vec()
|
||||
.drain(..)
|
||||
.map(|f| (f.name, f.size as _))
|
||||
.collect(),
|
||||
Self::get_files_for_audit(fs::JobType::Generic, r.files),
|
||||
json!({}),
|
||||
);
|
||||
self.file_transferred = true;
|
||||
@@ -2476,13 +2540,12 @@ impl Connection {
|
||||
}
|
||||
Some(file_action::Union::Cancel(c)) => {
|
||||
self.send_fs(ipc::FS::CancelWrite { id: c.id });
|
||||
if let Some(job) = fs::get_job_immutable(c.id, &self.read_jobs) {
|
||||
if let Some(job) = fs::remove_job(c.id, &mut self.read_jobs) {
|
||||
self.send_to_cm(ipc::Data::FileTransferLog((
|
||||
"transfer".to_string(),
|
||||
fs::serialize_transfer_job(job, false, true, ""),
|
||||
fs::serialize_transfer_job(&job, false, true, ""),
|
||||
)));
|
||||
}
|
||||
fs::remove_job(c.id, &mut self.read_jobs);
|
||||
}
|
||||
Some(file_action::Union::SendConfirm(r)) => {
|
||||
if let Some(job) = fs::get_job(r.id, &mut self.read_jobs) {
|
||||
@@ -3635,6 +3698,32 @@ impl Connection {
|
||||
fn try_empty_file_clipboard(&mut self) {
|
||||
try_empty_clipboard_files(ClipboardSide::Host, self.inner.id());
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
async fn send_printer_request(&mut self, data: Vec<u8>) {
|
||||
// This path is only used to identify the printer job.
|
||||
let path = format!("RustDesk://FsJob//Printer/{}", get_time());
|
||||
|
||||
let msg = fs::new_send(0, fs::JobType::Printer, path.clone(), 1, false);
|
||||
self.send(msg).await;
|
||||
self.printer_data
|
||||
.retain(|(t, _, _)| t.elapsed().as_secs() < 60);
|
||||
self.printer_data.push((Instant::now(), path, data));
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
async fn send_remote_printing_disallowed(&mut self) {
|
||||
let mut msg_out = Message::new();
|
||||
let res = MessageBox {
|
||||
msgtype: "custom-nook-nocancel-hasclose".to_owned(),
|
||||
title: "remote-printing-disallowed-tile-tip".to_owned(),
|
||||
text: "remote-printing-disallowed-text-tip".to_owned(),
|
||||
link: "".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
msg_out.set_message_box(res);
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
|
||||
@@ -3970,6 +4059,19 @@ fn start_wakelock_thread() -> std::sync::mpsc::Sender<(usize, usize)> {
|
||||
tx
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
pub fn on_printer_data(data: Vec<u8>) {
|
||||
crate::server::AUTHED_CONNS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|c| c.printer)
|
||||
.next()
|
||||
.map(|c| {
|
||||
c.sender.send(Data::PrinterData(data)).ok();
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub struct PortableState {
|
||||
pub last_uac: bool,
|
||||
@@ -4103,6 +4205,14 @@ impl Retina {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthedConn {
|
||||
pub conn_id: i32,
|
||||
pub conn_type: AuthConnType,
|
||||
pub session_key: SessionKey,
|
||||
pub sender: mpsc::UnboundedSender<Data>,
|
||||
pub printer: bool,
|
||||
}
|
||||
|
||||
mod raii {
|
||||
// ALIVE_CONNS: all connections, including unauthorized connections
|
||||
// AUTHED_CONNS: all authorized connections
|
||||
@@ -4127,11 +4237,23 @@ mod raii {
|
||||
pub struct AuthedConnID(i32, AuthConnType);
|
||||
|
||||
impl AuthedConnID {
|
||||
pub fn new(conn_id: i32, conn_type: AuthConnType, session_key: SessionKey) -> Self {
|
||||
AUTHED_CONNS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push((conn_id, conn_type, session_key));
|
||||
pub fn new(
|
||||
conn_id: i32,
|
||||
conn_type: AuthConnType,
|
||||
session_key: SessionKey,
|
||||
sender: mpsc::UnboundedSender<Data>,
|
||||
lr: LoginRequest,
|
||||
) -> Self {
|
||||
let printer = conn_type == crate::server::AuthConnType::Remote
|
||||
&& crate::is_support_remote_print(&lr.version)
|
||||
&& lr.my_platform == whoami::Platform::Windows.to_string();
|
||||
AUTHED_CONNS.lock().unwrap().push(AuthedConn {
|
||||
conn_id,
|
||||
conn_type,
|
||||
session_key,
|
||||
sender,
|
||||
printer,
|
||||
});
|
||||
Self::check_wake_lock();
|
||||
use std::sync::Once;
|
||||
static _ONCE: Once = Once::new();
|
||||
@@ -4153,7 +4275,7 @@ mod raii {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|c| c.1 == AuthConnType::Remote)
|
||||
.filter(|c| c.conn_type == AuthConnType::Remote)
|
||||
.count();
|
||||
allow_err!(WAKELOCK_SENDER
|
||||
.lock()
|
||||
@@ -4166,7 +4288,7 @@ mod raii {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|c| c.1 != AuthConnType::PortForward)
|
||||
.filter(|c| c.conn_type != AuthConnType::PortForward)
|
||||
.count()
|
||||
}
|
||||
|
||||
@@ -4179,16 +4301,16 @@ mod raii {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|c| c.0 == conn_id && c.1 == AuthConnType::Remote);
|
||||
.any(|c| c.conn_id == conn_id && c.conn_type == AuthConnType::Remote);
|
||||
// If there are 2 connections with the same peer_id and session_id, a remote connection and a file transfer or port forward connection,
|
||||
// If any of the connections is closed allowing retry, this will not be called;
|
||||
// If the file transfer/port forward connection is closed with no retry, the session should be kept for remote control menu action;
|
||||
// If the remote connection is closed with no retry, keep the session is not reasonable in case there is a retry button in the remote side, and ignore network fluctuations.
|
||||
let another_remote = AUTHED_CONNS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|c| c.0 != conn_id && c.2 == key && c.1 == AuthConnType::Remote);
|
||||
let another_remote = AUTHED_CONNS.lock().unwrap().iter().any(|c| {
|
||||
c.conn_id != conn_id
|
||||
&& c.session_key == key
|
||||
&& c.conn_type == AuthConnType::Remote
|
||||
});
|
||||
if is_remote || !another_remote {
|
||||
lock.remove(&key);
|
||||
log::info!("remove session");
|
||||
@@ -4256,12 +4378,12 @@ mod raii {
|
||||
.unwrap()
|
||||
.on_connection_close(self.0);
|
||||
}
|
||||
AUTHED_CONNS.lock().unwrap().retain(|c| c.0 != self.0);
|
||||
AUTHED_CONNS.lock().unwrap().retain(|c| c.conn_id != self.0);
|
||||
let remote_count = AUTHED_CONNS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|c| c.1 == AuthConnType::Remote)
|
||||
.filter(|c| c.conn_type == AuthConnType::Remote)
|
||||
.count();
|
||||
if remote_count == 0 {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
|
||||
@@ -505,7 +505,7 @@ pub fn try_stop_record_cursor_pos() {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|c| c.1 == AuthConnType::Remote)
|
||||
.filter(|c| c.conn_type == AuthConnType::Remote)
|
||||
.count();
|
||||
if remote_count > 0 {
|
||||
return;
|
||||
|
||||
@@ -812,7 +812,7 @@ pub mod client {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|c| c.1 == crate::server::AuthConnType::Remote)
|
||||
.filter(|c| c.conn_type == crate::server::AuthConnType::Remote)
|
||||
.count();
|
||||
stream.send(&Data::DataPortableService(ConnCount(Some(remote_count)))).await.ok();
|
||||
}
|
||||
|
||||
163
src/server/printer_service.rs
Normal file
163
src/server/printer_service.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
use super::service::{EmptyExtraFieldService, GenericService, Service};
|
||||
use hbb_common::{bail, dlopen::symbor::Library, log, ResultType};
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub const NAME: &'static str = "remote-printer";
|
||||
|
||||
const LIB_NAME_PRINTER_DRIVER_ADAPTER: &str = "printer_driver_adapter";
|
||||
|
||||
// Return 0 if success, otherwise return error code.
|
||||
pub type Init = fn(tag_name: *const i8) -> i32;
|
||||
pub type Uninit = fn();
|
||||
// dur_mills: Get the file generated in the last `dur_mills` milliseconds.
|
||||
// data: The raw prn data, xps format.
|
||||
// data_len: The length of the raw prn data.
|
||||
pub type GetPrnData = fn(dur_mills: u32, data: *mut *mut i8, data_len: *mut u32);
|
||||
|
||||
macro_rules! make_lib_wrapper {
|
||||
($($field:ident : $tp:ty),+) => {
|
||||
struct LibWrapper {
|
||||
_lib: Option<Library>,
|
||||
$($field: Option<$tp>),+
|
||||
}
|
||||
|
||||
impl LibWrapper {
|
||||
fn new() -> Self {
|
||||
let lib_name = match get_lib_name() {
|
||||
Ok(name) => name,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to get lib name, {}", e);
|
||||
return Self {
|
||||
_lib: None,
|
||||
$( $field: None ),+
|
||||
};
|
||||
}
|
||||
};
|
||||
let lib = match Library::open(&lib_name) {
|
||||
Ok(lib) => Some(lib),
|
||||
Err(e) => {
|
||||
log::warn!("Failed to load library {}, {}", &lib_name, e);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
$(let $field = if let Some(lib) = &lib {
|
||||
match unsafe { lib.symbol::<$tp>(stringify!($field)) } {
|
||||
Ok(m) => {
|
||||
log::info!("method found {}", stringify!($field));
|
||||
Some(*m)
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("Failed to load func {}, {}", stringify!($field), e);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};)+
|
||||
|
||||
Self {
|
||||
_lib: lib,
|
||||
$( $field ),+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LibWrapper {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
make_lib_wrapper!(
|
||||
init: Init,
|
||||
uninit: Uninit,
|
||||
get_prn_data: GetPrnData
|
||||
);
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref LIB_WRAPPER: Arc<Mutex<LibWrapper>> = Default::default();
|
||||
}
|
||||
|
||||
fn get_lib_name() -> ResultType<String> {
|
||||
let exe_file = std::env::current_exe()?;
|
||||
if let Some(cur_dir) = exe_file.parent() {
|
||||
let dll_name = format!("{}.dll", LIB_NAME_PRINTER_DRIVER_ADAPTER);
|
||||
let full_path = cur_dir.join(dll_name);
|
||||
if !full_path.exists() {
|
||||
bail!("{} not found", full_path.to_string_lossy().as_ref());
|
||||
} else {
|
||||
Ok(full_path.to_string_lossy().into_owned())
|
||||
}
|
||||
} else {
|
||||
bail!(
|
||||
"Invalid exe parent for {}",
|
||||
exe_file.to_string_lossy().as_ref()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(app_name: &str) -> ResultType<()> {
|
||||
let lib_wrapper = LIB_WRAPPER.lock().unwrap();
|
||||
let Some(fn_init) = lib_wrapper.init.as_ref() else {
|
||||
bail!("Failed to load func init");
|
||||
};
|
||||
|
||||
let tag_name = std::ffi::CString::new(app_name)?;
|
||||
let ret = fn_init(tag_name.as_ptr());
|
||||
if ret != 0 {
|
||||
bail!("Failed to init printer driver");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uninit() {
|
||||
let lib_wrapper = LIB_WRAPPER.lock().unwrap();
|
||||
if let Some(fn_uninit) = lib_wrapper.uninit.as_ref() {
|
||||
fn_uninit();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_prn_data(dur_mills: u32) -> ResultType<Vec<u8>> {
|
||||
let lib_wrapper = LIB_WRAPPER.lock().unwrap();
|
||||
if let Some(fn_get_prn_data) = lib_wrapper.get_prn_data.as_ref() {
|
||||
let mut data = std::ptr::null_mut();
|
||||
let mut data_len = 0u32;
|
||||
fn_get_prn_data(dur_mills, &mut data, &mut data_len);
|
||||
if data.is_null() || data_len == 0 {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let bytes =
|
||||
Vec::from(unsafe { std::slice::from_raw_parts(data as *const u8, data_len as usize) });
|
||||
unsafe {
|
||||
hbb_common::libc::free(data as *mut std::ffi::c_void);
|
||||
}
|
||||
Ok(bytes)
|
||||
} else {
|
||||
bail!("Failed to load func get_prn_file");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(name: String) -> GenericService {
|
||||
let svc = EmptyExtraFieldService::new(name, false);
|
||||
GenericService::run(&svc.clone(), run);
|
||||
svc.sp
|
||||
}
|
||||
|
||||
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
|
||||
while sp.ok() {
|
||||
let bytes = get_prn_data(1000)?;
|
||||
if !bytes.is_empty() {
|
||||
log::info!("Got prn data, data len: {}", bytes.len());
|
||||
crate::server::on_printer_data(bytes);
|
||||
}
|
||||
thread::sleep(Duration::from_millis(300));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -69,8 +69,6 @@ function getExt(name) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var jobIdCounter = 1;
|
||||
|
||||
class JobTable: Reactor.Component {
|
||||
this var jobs = [];
|
||||
this var job_map = {};
|
||||
@@ -126,8 +124,7 @@ class JobTable: Reactor.Component {
|
||||
}
|
||||
if (!to) return;
|
||||
to += handler.get_path_sep(!is_remote) + getFileName(is_remote, path);
|
||||
var id = jobIdCounter;
|
||||
jobIdCounter += 1;
|
||||
var id = handler.get_next_job_id();
|
||||
this.jobs.push({ type: "transfer",
|
||||
id: id, path: path, to: to,
|
||||
include_hidden: show_hidden,
|
||||
@@ -135,7 +132,7 @@ class JobTable: Reactor.Component {
|
||||
is_last: false
|
||||
});
|
||||
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
||||
handler.send_files(id, path, to, 0, show_hidden, is_remote);
|
||||
handler.send_files(id, 0, path, to, 0, show_hidden, is_remote);
|
||||
var self = this;
|
||||
self.timer(30ms, function() { self.update(); });
|
||||
}
|
||||
@@ -147,8 +144,8 @@ class JobTable: Reactor.Component {
|
||||
is_remote: is_remote, is_last: true, file_num: file_num };
|
||||
this.jobs.push(job);
|
||||
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
||||
jobIdCounter = id + 1;
|
||||
handler.add_job(id, path, to, file_num, show_hidden, is_remote);
|
||||
handler.update_next_job_id(id + 1);
|
||||
handler.add_job(id, 0, path, to, file_num, show_hidden, is_remote);
|
||||
stdout.println(JSON.stringify(job));
|
||||
}
|
||||
|
||||
@@ -162,16 +159,14 @@ class JobTable: Reactor.Component {
|
||||
}
|
||||
|
||||
function addDelDir(path, is_remote) {
|
||||
var id = jobIdCounter;
|
||||
jobIdCounter += 1;
|
||||
var id = handler.get_next_job_id();
|
||||
this.jobs.push({ type: "del-dir", id: id, path: path, is_remote: is_remote });
|
||||
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
||||
this.update();
|
||||
}
|
||||
|
||||
function addDelFile(path, is_remote) {
|
||||
var id = jobIdCounter;
|
||||
jobIdCounter += 1;
|
||||
var id = handler.get_next_job_id();
|
||||
this.jobs.push({ type: "del-file", id: id, path: path, is_remote: is_remote });
|
||||
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
||||
this.update();
|
||||
@@ -552,9 +547,9 @@ class FolderView : Reactor.Component {
|
||||
return;
|
||||
}
|
||||
var path = me.joinPath(name);
|
||||
handler.create_dir(jobIdCounter, path, me.is_remote);
|
||||
create_dir_jobs[jobIdCounter] = { is_remote: me.is_remote, path: path };
|
||||
jobIdCounter += 1;
|
||||
var id = handler.get_next_job_id();
|
||||
handler.create_dir(id, path, me.is_remote);
|
||||
create_dir_jobs[id] = { is_remote: me.is_remote, path: path };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -317,6 +317,9 @@ class MsgboxComponent: Reactor.Component {
|
||||
if (this.type == "multiple-sessions-nocancel") {
|
||||
values.sid = (this.$$(select))[0].value;
|
||||
}
|
||||
if (this.type == "remote-printer-selector") {
|
||||
values.name = (this.$$(select))[0].value;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
41
src/ui/printer.tis
Normal file
41
src/ui/printer.tis
Normal file
@@ -0,0 +1,41 @@
|
||||
include "sciter:reactor.tis";
|
||||
|
||||
handler.printerRequest = function(id, path) {
|
||||
show_printer_selector(id, path);
|
||||
};
|
||||
|
||||
function show_printer_selector(id, path)
|
||||
{
|
||||
var names = handler.get_printer_names();
|
||||
msgbox("remote-printer-selector", "Incoming Print Job", <PrinterComponent names={names} />, "", function(res=null) {
|
||||
if (res && res.name) {
|
||||
handler.on_printer_selected(id, path, res.name);
|
||||
}
|
||||
}, 180);
|
||||
}
|
||||
|
||||
class PrinterComponent extends Reactor.Component {
|
||||
this var names = [];
|
||||
this var jobTip = translate("print-incoming-job-confirm-tip");
|
||||
|
||||
function this(params) {
|
||||
if (params && params.names) {
|
||||
this.names = params.names;
|
||||
}
|
||||
}
|
||||
|
||||
function render() {
|
||||
return <div>
|
||||
<div>{translate("print-incoming-job-confirm-tip")}</div>
|
||||
<div style="margin-top: 1em;" />
|
||||
<div>
|
||||
<select style="width: 450; margin: 1em 0; font-size: 1.15em;">
|
||||
{this.names.map(function(name) {
|
||||
return <option value={name}>{name}</option>;
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-top: 1em;" />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
include "port_forward.tis";
|
||||
include "grid.tis";
|
||||
include "header.tis";
|
||||
include "printer.tis";
|
||||
</script>
|
||||
</head>
|
||||
<header>
|
||||
|
||||
@@ -379,6 +379,10 @@ impl InvokeUiSession for SciterHandler {
|
||||
fn update_record_status(&self, start: bool) {
|
||||
self.call("updateRecordStatus", &make_args!(start));
|
||||
}
|
||||
|
||||
fn printer_request(&self, id: i32, path: String) {
|
||||
self.call("printerRequest", &make_args!(id, path));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SciterSession(Session<SciterHandler>);
|
||||
@@ -491,6 +495,8 @@ impl sciter::EventHandler for SciterSession {
|
||||
fn get_chatbox();
|
||||
fn get_icon();
|
||||
fn get_home_dir();
|
||||
fn get_next_job_id();
|
||||
fn update_next_job_id(i32);
|
||||
fn read_dir(String, bool);
|
||||
fn remove_dir(i32, String, bool);
|
||||
fn create_dir(i32, String, bool);
|
||||
@@ -502,8 +508,8 @@ impl sciter::EventHandler for SciterSession {
|
||||
fn confirm_delete_files(i32, i32);
|
||||
fn set_no_confirm(i32);
|
||||
fn cancel_job(i32);
|
||||
fn send_files(i32, String, String, i32, bool, bool);
|
||||
fn add_job(i32, String, String, i32, bool, bool);
|
||||
fn send_files(i32, i32, String, String, i32, bool, bool);
|
||||
fn add_job(i32, i32, String, String, i32, bool, bool);
|
||||
fn resume_job(i32, bool);
|
||||
fn get_platform(bool);
|
||||
fn get_path_sep(bool);
|
||||
@@ -541,6 +547,8 @@ impl sciter::EventHandler for SciterSession {
|
||||
fn set_selected_windows_session_id(String);
|
||||
fn is_recording();
|
||||
fn has_file_clipboard();
|
||||
fn get_printer_names();
|
||||
fn on_printer_selected(i32, String, String);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,6 +850,22 @@ impl SciterSession {
|
||||
fn version_cmp(&self, v1: String, v2: String) -> i32 {
|
||||
(hbb_common::get_version_number(&v1) - hbb_common::get_version_number(&v2)) as i32
|
||||
}
|
||||
|
||||
fn get_printer_names(&self) -> Value {
|
||||
#[cfg(target_os = "windows")]
|
||||
let printer_names = crate::platform::windows::get_printer_names().unwrap_or_default();
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let printer_names: Vec<String> = vec![];
|
||||
let mut v = Value::array(0);
|
||||
for name in printer_names {
|
||||
v.push(name);
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn on_printer_selected(&self, id: i32, path: String, printer_name: String) {
|
||||
self.printer_response(id, path, printer_name);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_fd(id: i32, entries: &Vec<FileEntry>, only_count: bool) -> Value {
|
||||
|
||||
@@ -744,6 +744,8 @@ async fn handle_fs(
|
||||
tx: &UnboundedSender<Data>,
|
||||
tx_log: Option<&UnboundedSender<String>>,
|
||||
) {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use hbb_common::fs::serialize_transfer_job;
|
||||
|
||||
match fs {
|
||||
@@ -785,8 +787,9 @@ async fn handle_fs(
|
||||
// dummy remote, show_hidden, is_remote
|
||||
let mut job = fs::TransferJob::new_write(
|
||||
id,
|
||||
fs::JobType::Generic,
|
||||
"".to_string(),
|
||||
path,
|
||||
fs::DataSource::FilePath(PathBuf::from(&path)),
|
||||
file_num,
|
||||
false,
|
||||
false,
|
||||
@@ -805,27 +808,24 @@ async fn handle_fs(
|
||||
write_jobs.push(job);
|
||||
}
|
||||
ipc::FS::CancelWrite { id } => {
|
||||
if let Some(job) = fs::get_job(id, write_jobs) {
|
||||
if let Some(job) = fs::remove_job(id, write_jobs) {
|
||||
job.remove_download_file();
|
||||
tx_log.map(|tx: &UnboundedSender<String>| {
|
||||
tx.send(serialize_transfer_job(job, false, true, ""))
|
||||
tx.send(serialize_transfer_job(&job, false, true, ""))
|
||||
});
|
||||
fs::remove_job(id, write_jobs);
|
||||
}
|
||||
}
|
||||
ipc::FS::WriteDone { id, file_num } => {
|
||||
if let Some(job) = fs::get_job(id, write_jobs) {
|
||||
if let Some(job) = fs::remove_job(id, write_jobs) {
|
||||
job.modify_time();
|
||||
send_raw(fs::new_done(id, file_num), tx);
|
||||
tx_log.map(|tx| tx.send(serialize_transfer_job(job, true, false, "")));
|
||||
fs::remove_job(id, write_jobs);
|
||||
tx_log.map(|tx| tx.send(serialize_transfer_job(&job, true, false, "")));
|
||||
}
|
||||
}
|
||||
ipc::FS::WriteError { id, file_num, err } => {
|
||||
if let Some(job) = fs::get_job(id, write_jobs) {
|
||||
tx_log.map(|tx| tx.send(serialize_transfer_job(job, false, false, &err)));
|
||||
if let Some(job) = fs::remove_job(id, write_jobs) {
|
||||
tx_log.map(|tx| tx.send(serialize_transfer_job(&job, false, false, &err)));
|
||||
send_raw(fs::new_error(job.id(), err, file_num), tx);
|
||||
fs::remove_job(job.id(), write_jobs);
|
||||
}
|
||||
}
|
||||
ipc::FS::WriteBlock {
|
||||
@@ -871,32 +871,34 @@ async fn handle_fs(
|
||||
..Default::default()
|
||||
};
|
||||
if let Some(file) = job.files().get(file_num as usize) {
|
||||
let path = get_string(&job.join(&file.name));
|
||||
match is_write_need_confirmation(&path, &digest) {
|
||||
Ok(digest_result) => {
|
||||
match digest_result {
|
||||
DigestCheckResult::IsSame => {
|
||||
req.set_skip(true);
|
||||
let msg_out = new_send_confirm(req);
|
||||
send_raw(msg_out, &tx);
|
||||
}
|
||||
DigestCheckResult::NeedConfirm(mut digest) => {
|
||||
// upload to server, but server has the same file, request
|
||||
digest.is_upload = is_upload;
|
||||
let mut msg_out = Message::new();
|
||||
let mut fr = FileResponse::new();
|
||||
fr.set_digest(digest);
|
||||
msg_out.set_file_response(fr);
|
||||
send_raw(msg_out, &tx);
|
||||
}
|
||||
DigestCheckResult::NoSuchFile => {
|
||||
let msg_out = new_send_confirm(req);
|
||||
send_raw(msg_out, &tx);
|
||||
if let fs::DataSource::FilePath(p) = &job.data_source {
|
||||
let path = get_string(&fs::TransferJob::join(p, &file.name));
|
||||
match is_write_need_confirmation(&path, &digest) {
|
||||
Ok(digest_result) => {
|
||||
match digest_result {
|
||||
DigestCheckResult::IsSame => {
|
||||
req.set_skip(true);
|
||||
let msg_out = new_send_confirm(req);
|
||||
send_raw(msg_out, &tx);
|
||||
}
|
||||
DigestCheckResult::NeedConfirm(mut digest) => {
|
||||
// upload to server, but server has the same file, request
|
||||
digest.is_upload = is_upload;
|
||||
let mut msg_out = Message::new();
|
||||
let mut fr = FileResponse::new();
|
||||
fr.set_digest(digest);
|
||||
msg_out.set_file_response(fr);
|
||||
send_raw(msg_out, &tx);
|
||||
}
|
||||
DigestCheckResult::NoSuchFile => {
|
||||
let msg_out = new_send_confirm(req);
|
||||
send_raw(msg_out, &tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
send_raw(fs::new_error(id, err, file_num), &tx);
|
||||
Err(err) => {
|
||||
send_raw(fs::new_error(id, err, file_num), &tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ pub struct Session<T: InvokeUiSession> {
|
||||
pub server_clipboard_enabled: Arc<RwLock<bool>>,
|
||||
pub last_change_display: Arc<Mutex<ChangeDisplayRecord>>,
|
||||
pub connection_round_state: Arc<Mutex<ConnectionRoundState>>,
|
||||
pub printer_names: Arc<RwLock<HashMap<i32, String>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -1505,6 +1506,20 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
pub fn get_conn_token(&self) -> Option<String> {
|
||||
self.lc.read().unwrap().get_conn_token()
|
||||
}
|
||||
|
||||
pub fn printer_response(&self, id: i32, path: String, printer_name: String) {
|
||||
self.printer_names.write().unwrap().insert(id, printer_name);
|
||||
let to = std::env::temp_dir().join(format!("rustdesk_printer_{id}"));
|
||||
self.send(Data::SendFiles((
|
||||
id,
|
||||
hbb_common::fs::JobType::Printer,
|
||||
path,
|
||||
to.to_string_lossy().to_string(),
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
||||
@@ -1570,6 +1585,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
||||
fn is_multi_ui_session(&self) -> bool;
|
||||
fn update_record_status(&self, start: bool);
|
||||
fn update_empty_dirs(&self, _res: ReadEmptyDirsResponse) {}
|
||||
fn printer_request(&self, id: i32, path: String);
|
||||
}
|
||||
|
||||
impl<T: InvokeUiSession> Deref for Session<T> {
|
||||
|
||||
Reference in New Issue
Block a user