mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-07 12:13:20 +00:00
craft-print: printer_queues lists the queues without asking any driver
On Windows, printers() reads each driver's PrintCapabilities to report two-sided support before it returns. A networked printer in power save can hold that query for a long time, so a Print dialog showed no printer at all, not even the ones that were awake (storytold/wordcraft#5710, storytold/wordcraft#5645, storytold/wordcraft#5413). printer_queues() returns the same queues and default from EnumPrinters and GetDefaultPrinter with two_sided unknown, and makes no driver call. Submission still checks the job's options with the driver. On other platforms it is printers(). printers() is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a8b996d207
commit
62b0fe5edb
@@ -37,7 +37,10 @@ opens Windows printer preferences and submits jobs with copies, collation, duple
|
||||
- **Windows:** in this process, with no PowerShell, script or other program started, so
|
||||
printing works where App Control for Business or AppLocker enforce script rules. Printers
|
||||
come from `EnumPrinters` (local queues and `\\server\queue` connections) and the default from
|
||||
`GetDefaultPrinter`. A job is drawn as vectors by the in-box `Windows.Data.Pdf` with Direct2D
|
||||
`GetDefaultPrinter`. `printers` also reads each driver's PrintCapabilities to report two-sided
|
||||
support, which can wait on the device (a networked printer in power save); `printer_queues`
|
||||
lists the same queues without asking any driver, so a Print dialog's list never waits on a
|
||||
device. A job is drawn as vectors by the in-box `Windows.Data.Pdf` with Direct2D
|
||||
into the XPS print API (the `winprint` crate), and copies, collation, duplex, colour, paper,
|
||||
orientation and "no scaling" travel in a PrintTicket that the driver validates first; a
|
||||
driver that would change the paper, orientation or scaling is refused before anything is
|
||||
|
||||
@@ -422,6 +422,22 @@ pub fn printers() -> Vec<Printer> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The printers the system knows, without asking their drivers anything: [`printers`] with
|
||||
/// `two_sided` unknown. On Windows [`printers`] reads each driver's PrintCapabilities, which can
|
||||
/// wait on the device (a networked printer in power save), so a Print dialog that only needs
|
||||
/// names lists them from this without that wait. Submission still checks the job's options with
|
||||
/// the driver. Elsewhere it is [`printers`], whose `two_sided` is already unknown.
|
||||
pub fn printer_queues() -> Vec<Printer> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
windows::printer_queues()
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
printers()
|
||||
}
|
||||
}
|
||||
|
||||
/// The Print dialog's printers from lpstat's two answers: the spooler's queues (`-p -d`), then
|
||||
/// the driverless destinations of `available` (`-e`) that have no queue — CUPS builds a temporary
|
||||
/// queue when `lp` sends them a job. Each destination once, queues first; the default still comes
|
||||
|
||||
@@ -410,6 +410,8 @@ fn windows_jobs_reach_the_spooler() {
|
||||
return;
|
||||
}
|
||||
let listed = spool::printers().into_iter().find(|p| p.name == name);
|
||||
// The list a Print dialog shows first: the same queue, named without asking its driver.
|
||||
let queue = spool::printer_queues().into_iter().find(|p| p.name == name);
|
||||
let mixed = test_pdf(&[LETTER, LETTER_LANDSCAPE, LETTER], true);
|
||||
let job = Job { printer: Some(name.into()), title: "mixed.pdf".into(), ..Job::default() };
|
||||
let caller = std::thread::current().id();
|
||||
@@ -450,6 +452,7 @@ fn windows_jobs_reach_the_spooler() {
|
||||
name,
|
||||
);
|
||||
assert!(listed.as_ref().is_some_and(|p| p.two_sided.is_some()), "the new printer is listed, its duplex support known: {listed:?}");
|
||||
assert!(queue.as_ref().is_some_and(|p| p.two_sided.is_none()), "the new printer is listed with no driver query: {queue:?}");
|
||||
assert_eq!(sent.as_deref(), Ok("spooled 3 sheet(s)"), "{sent:?}");
|
||||
if let Some(refused) = duplex_refused {
|
||||
assert!(matches!(refused, Err(Error::Spool(ref m)) if m.contains("simplex/duplex") && m.contains("nothing was sent")), "{refused:?}");
|
||||
|
||||
@@ -43,6 +43,17 @@ fn print_ticket_provider() -> MutexGuard<'static, ()> {
|
||||
}
|
||||
|
||||
pub(crate) fn printers() -> Vec<Printer> {
|
||||
spooler_list(two_sided)
|
||||
}
|
||||
|
||||
/// The queues and the default without asking any driver: a networked printer asleep in power
|
||||
/// save can hold its driver's PrintCapabilities query for a long time, and the Print dialog
|
||||
/// must not wait on it to show the queue (or any other).
|
||||
pub(crate) fn printer_queues() -> Vec<Printer> {
|
||||
spooler_list(|_| None)
|
||||
}
|
||||
|
||||
fn spooler_list(two_sided: impl Fn(&PrinterDevice) -> Option<bool>) -> Vec<Printer> {
|
||||
match PrinterDevice::all() {
|
||||
Ok(devices) => crate::printer_list(devices.iter().map(|d| (d.name().to_string(), two_sided(d))), default_printer().as_deref()),
|
||||
Err(_) => Vec::new(),
|
||||
|
||||
Reference in New Issue
Block a user