From 9ba24930278e78a4d36f0601330389ab58e43be9 Mon Sep 17 00:00:00 2001 From: mechpanos <25664832+mechpanos@users.noreply.github.com> Date: Mon, 28 Sep 2026 15:40:39 +0300 Subject: [PATCH] fix(print): open the driver's printing preferences without blocking the event loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- locales/ar-SA/opencadstudio.ftl | 2 - locales/bg-BG/opencadstudio.ftl | 2 - locales/cs-CZ/opencadstudio.ftl | 2 - locales/de-DE/opencadstudio.ftl | 2 - locales/el-GR/opencadstudio.ftl | 2 - locales/en-US/opencadstudio.ftl | 2 - locales/es-ES/opencadstudio.ftl | 2 - locales/fi-FI/opencadstudio.ftl | 2 - locales/fr-FR/opencadstudio.ftl | 2 - locales/hi-IN/opencadstudio.ftl | 2 - locales/hu-HU/opencadstudio.ftl | 2 - locales/it-IT/opencadstudio.ftl | 2 - locales/ja-JP/opencadstudio.ftl | 2 - locales/ko-KR/opencadstudio.ftl | 2 - locales/nl-NL/opencadstudio.ftl | 2 - locales/pl-PL/opencadstudio.ftl | 2 - locales/pt-BR/opencadstudio.ftl | 2 - locales/ru-RU/opencadstudio.ftl | 2 - locales/tr-TR/opencadstudio.ftl | 2 - locales/zh-CN/opencadstudio.ftl | 2 - locales/zh-TW/opencadstudio.ftl | 2 - src/app/update/file.rs | 55 +++++------------ src/io/print_to_printer.rs | 102 ++++++-------------------------- src/locale_catalog.rs | 2 - 24 files changed, 35 insertions(+), 166 deletions(-) diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 8b7dd9902..ccf139682 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -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 = سماكات خطوط الكائنات diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 3d88014b0..9348ddf70 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -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 = Дебелини на линии на обект diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 67aaf37cf..f601cc07c 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -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ů diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index f32c9ba95..834163d70 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -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 diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index 1729d534a..38598e4b1 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -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 = Πάχη γραμμών αντικειμένων diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 05e319474..c3fdc9b1f 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -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 diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 92fd93eff..8c2eafe16 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -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 diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 058b1a6a5..2603294c5 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -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 diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 7607d15b3..571898097 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -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 diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 4929071d4..0dd4beca2 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -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 = ऑब्जेक्ट लाइनवेट diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 08ce8b3d0..63cd2e766 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -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 diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index 1994c37d3..1bb193b14 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -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 diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index d9daa920a..fcbb914c8 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -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 = オブジェクトの線の太さ diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 8a537142a..326aa897d 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -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 = 객체 선가중치 diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 314a83d35..2c7cf12a9 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -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 diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index ef384bbc5..01f9ffd36 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -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 diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index cb5ea8a3e..381733ba6 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -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 diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 42a3d5334..50f75c2f4 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -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 = Весы для объектов diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index eb92c4bcb..3cb2f3690 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -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ı diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 828fc7429..52a18c6ea 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -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 = 对象线宽 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index bbf1928ca..a35cb8ff8 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -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 = 物件線寬 diff --git a/src/app/update/file.rs b/src/app/update/file.rs index 6a49b66d4..1fd84bbf1 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -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 { // "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 { - 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()), diff --git a/src/io/print_to_printer.rs b/src/io/print_to_printer.rs index 29f6578da..16c89a9a8 100644 --- a/src/io/print_to_printer.rs +++ b/src/io/print_to_printer.rs @@ -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 { - 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 = 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::(), - 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::(), - current.as_ptr().cast::(), - 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::(), - }; - // 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::(), 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::*; diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 92edc4613..7edd4575b 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -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")),