forked from 1b-agent/opencadstudio-upstream
fix(print): open the driver's printing preferences without blocking the event loop
Properties… in the Plot dialog froze the application on Windows. `on_printer_properties` called `DocumentPropertiesW` with `DM_IN_PROMPT` from inside the closure handed to `iced::window::run`, and iced runs that closure synchronously on the winit event-loop thread. The driver's property sheet wants to run its own modal message loop on that same thread, so the two loops collide: the sheet is never created at all, the window stops painting, and the thread spins at 100% of one core with no way back. Measured on Windows 11 x86-64: the pinned thread is the window's own UI thread, and compstui.dll plus the driver's PrintConfig.dll are loaded into the process, so execution does reach the driver's property-sheet code and never leaves it. It reproduces with a physical printer as readily as with Microsoft Print to PDF, so the mechanism was broken for every printer, not only the virtual one named in the report. The system's own printing-preferences UI is now started as a separate process, which pumps its own messages and leaves ours free, so the freeze is impossible by construction rather than merely unlikely. The command that does it already existed for the case where no printer is selected; it now serves a named printer too, and the in-process modal path is gone. Preferences still reach the job: the sheet writes the per-user DEVMODE that the plot path reads back through `DocumentPropertiesW`, which was confirmed by changing the orientation in the sheet and watching the value the plot path reads change with it. The new test pins down that the printer name must reach `printui` as a single argument: split across several it matches nothing and the process exits 0 without drawing anything, which would be a failure with nothing to show the user. Fixes #1474 Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
d40e6c8d1d
commit
9ba2493027
24 files changed
+35
-166
No files matched your search
@@ -5243,8 +5243,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = دمج الخطوط المتداخلة
|
||||
.object-lineweights = سماكات خطوط الكائنات
|
||||
|
||||
@@ -5231,8 +5231,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Обедини припокриващи се линии
|
||||
.object-lineweights = Дебелини на линии на обект
|
||||
|
||||
@@ -5228,8 +5228,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Sloučit překrývající se čáry
|
||||
.object-lineweights = Tloušťky čar objektů
|
||||
|
||||
@@ -5229,8 +5229,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Zusammenführung überlappender Leitungen
|
||||
.object-lineweights = Objektliniengewichte
|
||||
|
||||
@@ -5231,8 +5231,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = Ο εκτυπωτής δεν αναφέρει επιλογές driver.
|
||||
.could-not-read-the-printer-s-options-error = Αδυναμία ανάγνωσης των επιλογών του εκτυπωτή: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Ανάγνωση των επιλογών του εκτυπωτή…
|
||||
.printing-preferences-saved-for-printer = Οι προτιμήσεις εκτύπωσης αποθηκεύτηκαν για τον εκτυπωτή __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Οι προτιμήσεις εκτύπωσης δεν άλλαξαν.
|
||||
.printer-properties = Ιδιότητες εκτυπωτή
|
||||
.merge-overlapping-lines = Συγχώνευση επικαλυπτόμενων γραμμών
|
||||
.object-lineweights = Πάχη γραμμών αντικειμένων
|
||||
|
||||
@@ -5232,8 +5232,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Merge overlapping lines
|
||||
.object-lineweights = Object lineweights
|
||||
|
||||
@@ -5231,8 +5231,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Fusionar líneas solapadas
|
||||
.object-lineweights = Grosores de línea de objetos
|
||||
|
||||
@@ -5230,8 +5230,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Yhdistä päällekkäiset rivit
|
||||
.object-lineweights = Objektin viivan painot
|
||||
|
||||
@@ -5231,8 +5231,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Fusionner les lignes qui se chevauchent
|
||||
.object-lineweights = Poids de ligne d'objet
|
||||
|
||||
@@ -5219,8 +5219,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = मर्ज ओवरलैपिंग लाइन
|
||||
.object-lineweights = ऑब्जेक्ट लाइनवेट
|
||||
|
||||
@@ -5230,8 +5230,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Egyesítse az átfedő vonalakat
|
||||
.object-lineweights = Objektumvonalvastagságok
|
||||
|
||||
@@ -5230,8 +5230,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Unisci linee sovrapposte
|
||||
.object-lineweights = Spessori linea degli oggetti
|
||||
|
||||
@@ -5219,8 +5219,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = 重複線を結合
|
||||
.object-lineweights = オブジェクトの線の太さ
|
||||
|
||||
@@ -5230,8 +5230,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = 겹치는 선 병합
|
||||
.object-lineweights = 객체 선가중치
|
||||
|
||||
@@ -5229,8 +5229,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Overlaplijnen samenvoegen
|
||||
.object-lineweights = Objectlijndiktes
|
||||
|
||||
@@ -5230,8 +5230,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Połącz nakładające się linie
|
||||
.object-lineweights = Szerokości linii obiektu
|
||||
|
||||
@@ -5231,8 +5231,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Mesclar linhas sobrepostas
|
||||
.object-lineweights = Espessuras de linha dos objetos
|
||||
|
||||
@@ -5232,8 +5232,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Слияние пересекающихся линий
|
||||
.object-lineweights = Весы для объектов
|
||||
|
||||
@@ -5171,8 +5171,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = Çakışan çizgileri birleştir
|
||||
.object-lineweights = Nesne çizgi kalınlıkları
|
||||
|
||||
@@ -5214,8 +5214,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = 合并重叠线
|
||||
.object-lineweights = 对象线宽
|
||||
|
||||
@@ -5226,8 +5226,6 @@ plot =
|
||||
.this-printer-reports-no-driver-options = This printer reports no driver options.
|
||||
.could-not-read-the-printer-s-options-error = Could not read the printer's options: __ocs_fmt_0__
|
||||
.reading-the-printer-s-options = Reading the printer's options…
|
||||
.printing-preferences-saved-for-printer = Printing preferences saved for __ocs_fmt_0__.
|
||||
.printing-preferences-unchanged = Printing preferences unchanged.
|
||||
.printer-properties = Printer properties
|
||||
.merge-overlapping-lines = 合併重疊線
|
||||
.object-lineweights = 物件線寬
|
||||
|
||||
+16
-39
@@ -4414,9 +4414,9 @@ impl OpenCADStudio {
|
||||
///
|
||||
/// On CUPS platforms (Linux, macOS) the driver's options are listed in an
|
||||
/// in-line editor and applied to every job for that printer through
|
||||
/// `lp -o`. On Windows the driver's own document-properties sheet is
|
||||
/// shown and its result stored as the printer's preferences, which the
|
||||
/// print job honours. Without a named printer, or where the options
|
||||
/// `lp -o`. On Windows the system opens the driver's own printing
|
||||
/// preferences in its own process, and the print job honours what the
|
||||
/// user saves there. Without a named printer, or where the options
|
||||
/// cannot be listed, the platform's printer settings open instead.
|
||||
fn on_printer_properties(&mut self) -> Task<Message> {
|
||||
// "Default" is a real printer: its preferences sheet is the one to
|
||||
@@ -4427,12 +4427,18 @@ impl OpenCADStudio {
|
||||
.clone()
|
||||
.or_else(|| self.plot_dialog.default_printer.clone());
|
||||
let Some(printer) = chosen.filter(|_| !self.plot_dialog.to_file) else {
|
||||
self.open_printer_settings_fallback();
|
||||
self.open_printer_settings(None);
|
||||
return Task::none();
|
||||
};
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
return self.edit_windows_printer_preferences(printer);
|
||||
// The driver's sheet is modal: it pumps messages itself until the
|
||||
// user closes it. Hosted on our event-loop thread it starves the
|
||||
// loop it is sharing, so the window stops painting and a core
|
||||
// spins. The system's own preferences process pumps its own
|
||||
// messages instead, which leaves ours free.
|
||||
self.open_printer_settings(Some(&printer));
|
||||
return Task::none();
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
@@ -4454,40 +4460,11 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
/// The driver's document-properties sheet, owned by the main window so
|
||||
/// it sits on top of the plot dialog.
|
||||
#[cfg(target_os = "windows")]
|
||||
fn edit_windows_printer_preferences(&mut self, printer: String) -> Task<Message> {
|
||||
let Some(window) = self.main_window else {
|
||||
self.open_printer_settings_fallback();
|
||||
return Task::none();
|
||||
};
|
||||
iced::window::run(window, move |w| {
|
||||
use iced::window::raw_window_handle::RawWindowHandle;
|
||||
let owner = match w.window_handle().ok().map(|h| h.as_raw()) {
|
||||
Some(RawWindowHandle::Win32(handle)) => handle.hwnd.get(),
|
||||
_ => 0,
|
||||
};
|
||||
match crate::io::print_to_printer::edit_printer_preferences(&printer, owner) {
|
||||
Ok(true) => Ok(crate::tf!("Printing preferences saved for {printer}.").into_owned()),
|
||||
Ok(false) => Ok(crate::t!("Printing preferences unchanged.").into_owned()),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
})
|
||||
.then(|result| {
|
||||
Task::done(Message::BackgroundIoFinished(
|
||||
result.map(|message| message),
|
||||
false,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// The platform's printer settings surface: a settings panel on Linux,
|
||||
/// the printers control panel on Windows, System Settings on macOS.
|
||||
fn open_printer_settings_fallback(&mut self) {
|
||||
match crate::io::print_to_printer::open_printer_properties(
|
||||
self.plot_dialog.printer.as_deref(),
|
||||
) {
|
||||
/// The platform's printer settings surface: the driver's printing
|
||||
/// preferences for `printer`, or — without one — a settings panel on
|
||||
/// Linux, the printers control panel on Windows, System Settings on macOS.
|
||||
fn open_printer_settings(&mut self, printer: Option<&str>) {
|
||||
match crate::io::print_to_printer::open_printer_properties(printer) {
|
||||
Ok(()) => self
|
||||
.command_line
|
||||
.push_info(crate::t!("Opened printer properties.").as_ref()),
|
||||
|
||||
+19
-83
@@ -1518,6 +1518,25 @@ mod printer_properties_tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// `printui` parses the raw command line itself, so a name that reaches it
|
||||
/// split over several arguments matches no printer and it exits 0 without
|
||||
/// drawing anything — a failure with nothing to show the user. The name
|
||||
/// has to stay a single argument, directly after `/n`. The verb matters
|
||||
/// too: `/e` is printing preferences, `/p` the device's admin sheet.
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn windows_preferences_keep_the_printer_name_in_one_argument() {
|
||||
for name in ["Office Printer", "Wide Format Plotter 1200", "Εκτυπωτής γραφείου"] {
|
||||
let (program, args) = printer_properties_command(Some(name));
|
||||
assert_eq!(program, "rundll32.exe");
|
||||
assert!(args.contains(&"/e".to_string()), "{args:?} must ask for preferences");
|
||||
assert!(!args.contains(&"/p".to_string()), "{args:?} must not ask for properties");
|
||||
let flag = args.iter().position(|arg| arg == "/n").expect("/n introduces the name");
|
||||
assert_eq!(args.get(flag + 1).map(String::as_str), Some(name), "{args:?}");
|
||||
assert_eq!(args.iter().filter(|arg| arg.as_str() == name).count(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[test]
|
||||
fn macos_opens_print_settings() {
|
||||
@@ -1722,89 +1741,6 @@ pub fn open_desktop_printer_settings(printer: Option<&str>) -> Result<(), String
|
||||
.map_err(|error| format!("Could not open printer settings: {error}"))
|
||||
}
|
||||
|
||||
/// Show the driver's own document-properties sheet for `printer`, owned by
|
||||
/// the window `owner` (an `HWND`, 0 for none), and on OK store the result as
|
||||
/// the user's printing preferences for that printer — which is what the
|
||||
/// print job then honours. Returns `Ok(false)` when the user cancelled.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn edit_printer_preferences(printer: &str, owner: isize) -> Result<bool, String> {
|
||||
use windows_sys::Win32::Graphics::Gdi::DEVMODEW;
|
||||
use windows_sys::Win32::Graphics::Printing::{
|
||||
ClosePrinter, DocumentPropertiesW, OpenPrinterW, SetPrinterW, PRINTER_HANDLE,
|
||||
PRINTER_INFO_9W,
|
||||
};
|
||||
// winspool.h: DM_OUT_BUFFER = DM_COPY, DM_IN_PROMPT = DM_PROMPT,
|
||||
// DM_IN_BUFFER = DM_MODIFY.
|
||||
const DM_OUT_BUFFER: u32 = 2;
|
||||
const DM_IN_PROMPT: u32 = 4;
|
||||
const DM_IN_BUFFER: u32 = 8;
|
||||
const IDOK: i32 = 1;
|
||||
|
||||
let name: Vec<u16> = printer.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
let mut handle = PRINTER_HANDLE {
|
||||
Value: std::ptr::null_mut(),
|
||||
};
|
||||
// SAFETY: `name` is NUL-terminated and outlives the call; a null default
|
||||
// asks for PRINTER_ACCESS_USE, enough for per-user preferences.
|
||||
if unsafe { OpenPrinterW(name.as_ptr(), &mut handle, std::ptr::null()) } == 0 {
|
||||
return Err(format!("Could not open printer \"{printer}\""));
|
||||
}
|
||||
let owner = owner as windows_sys::Win32::Foundation::HWND;
|
||||
let outcome = (|| {
|
||||
// SAFETY: with null buffers the call only reports the DEVMODE size.
|
||||
let size = unsafe {
|
||||
DocumentPropertiesW(owner, handle, name.as_ptr(), std::ptr::null_mut(), std::ptr::null(), 0)
|
||||
};
|
||||
if size <= 0 {
|
||||
return Err(format!("Printer \"{printer}\" reports no document properties"));
|
||||
}
|
||||
// DEVMODEW is u16-aligned; a u16 buffer keeps the cast sound.
|
||||
let words = (size as usize).div_ceil(2);
|
||||
let mut current = vec![0u16; words];
|
||||
let mut edited = vec![0u16; words];
|
||||
// SAFETY: both buffers hold `size` bytes as the driver requested.
|
||||
let fetched = unsafe {
|
||||
DocumentPropertiesW(
|
||||
owner,
|
||||
handle,
|
||||
name.as_ptr(),
|
||||
current.as_mut_ptr().cast::<DEVMODEW>(),
|
||||
std::ptr::null(),
|
||||
DM_OUT_BUFFER,
|
||||
)
|
||||
};
|
||||
if fetched < 0 {
|
||||
return Err(format!("Could not read the preferences of \"{printer}\""));
|
||||
}
|
||||
edited.copy_from_slice(¤t);
|
||||
// SAFETY: as above; DM_IN_PROMPT runs the driver's modal sheet.
|
||||
let response = unsafe {
|
||||
DocumentPropertiesW(
|
||||
owner,
|
||||
handle,
|
||||
name.as_ptr(),
|
||||
edited.as_mut_ptr().cast::<DEVMODEW>(),
|
||||
current.as_ptr().cast::<DEVMODEW>(),
|
||||
DM_IN_BUFFER | DM_IN_PROMPT | DM_OUT_BUFFER,
|
||||
)
|
||||
};
|
||||
if response != IDOK {
|
||||
return Ok(false);
|
||||
}
|
||||
let info = PRINTER_INFO_9W {
|
||||
pDevMode: edited.as_mut_ptr().cast::<DEVMODEW>(),
|
||||
};
|
||||
// SAFETY: level 9 takes a PRINTER_INFO_9W; the DEVMODE outlives the call.
|
||||
if unsafe { SetPrinterW(handle, 9, (&info as *const PRINTER_INFO_9W).cast::<u8>(), 0) } == 0 {
|
||||
return Err(format!("Could not save the preferences of \"{printer}\""));
|
||||
}
|
||||
Ok(true)
|
||||
})();
|
||||
// SAFETY: `handle` came from OpenPrinterW above.
|
||||
unsafe { ClosePrinter(handle) };
|
||||
outcome
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod driver_option_tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1498,8 +1498,6 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
||||
"This printer reports no driver options." => Some(("plot", "this-printer-reports-no-driver-options")),
|
||||
"Could not read the printer's options: {error}" => Some(("plot", "could-not-read-the-printer-s-options-error")),
|
||||
"Reading the printer's options…" => Some(("plot", "reading-the-printer-s-options")),
|
||||
"Printing preferences saved for {printer}." => Some(("plot", "printing-preferences-saved-for-printer")),
|
||||
"Printing preferences unchanged." => Some(("plot", "printing-preferences-unchanged")),
|
||||
"Printer properties" => Some(("plot", "printer-properties")),
|
||||
"Layout renamed: '{old_name}' → '{new_name}'" => Some(("common", "layout-renamed-old-name-new-name")),
|
||||
"Layouts" => Some(("layout", "layouts")),
|
||||
|
||||
Reference in new issue
Block a user