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
+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()),
|
||||
|
||||
Reference in new issue
Block a user