From 2b7e62f901e66ea33e37d65459a9737d37fd403b Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Sun, 31 May 2026 13:01:52 +0200 Subject: [PATCH] [PATCH] print: fix black screen on macOS arm64 after PDF export On macOS arm64 (Apple Silicon, Sequoia), exporting a PDF via QPrintPreviewWidget leaves a black screen with only the mouse cursor visible. Cmd+Tab restores the display; the exported PDF itself is correct and clickable cross-reference links work fine. Root cause ---------- requestPaint() is a slot connected to QPrintPreviewWidget::paintRequested. Inside this slot the code was calling painter.end() manually, then pdfConvertUriToGoTo(). On macOS arm64 the Qt5 paint cycle backed by Metal/CALayer is asynchronous: closing the QPainter from *within* the paintRequested slot interrupts the compositor before it has flushed the backing store. The window goes black and never repaints because the close() that follows immediately destroys it. On x86_64 / older macOS (raster/CoreGraphics backend) the paint cycle is synchronous, so the same code happened to work. Fix --- 1. Remove the manual painter.end() and pdfConvertUriToGoTo() call from requestPaint(). The QPainter is stack-allocated; it destructs normally when the slot returns, which is the correct moment to flush the PDF. 2. In print(), capture the output file name before m_preview->print(), then defer both pdfConvertUriToGoTo() and this->close() to the next event-loop iteration via QTimer::singleShot(0, ...). This gives the Metal compositor one full event-loop turn to finish compositing the backing store before the window is torn down. The fix is a no-op on all other platforms: QTimer::singleShot(0) posts an event that fires in the very next iteration, so there is no perceptible delay. Tested ------ - macOS Sequoia 15.x, Apple M-series, Qt 5.15.x (arm64): black screen gone - macOS 10.15 x86_64 VM, Qt 5.15.x: no regression - Linux/Debian Qt 5.15.x: no regression - PDF cross-reference links and GoTo/FitR destinations: unaffected Fixes: black screen after PDF export on macOS arm64 --- sources/print/projectprintwindow.cpp | 38 +++++++++++++++++++++------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/sources/print/projectprintwindow.cpp b/sources/print/projectprintwindow.cpp index 27364d26d..01c1e5940 100644 --- a/sources/print/projectprintwindow.cpp +++ b/sources/print/projectprintwindow.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include /** @@ -495,13 +496,12 @@ void ProjectPrintWindow::requestPaint() printDiagram(diagram, ui->m_fit_in_page_cb->isChecked(), &painter, m_printer, diagramPageMap); } - if (pdfExport) { - painter.end(); // flush & close the PDF file on disk - // Convert URI link annotations into native internal GoTo/FitR actions: - // cross-references then jump inside the document (no new viewer - // instance) and frame the target element. - pdfConvertUriToGoTo(m_printer->outputFileName()); - } + // Note: do NOT call painter.end() or pdfConvertUriToGoTo() here. + // We are inside the paintRequested slot: the QPrintPreviewWidget still + // owns the paint cycle. On macOS arm64 (Metal/CALayer compositor), + // closing the QPainter manually inside this slot leaves the backing + // store in an undefined state, producing a black screen after export. + // pdfConvertUriToGoTo() is deferred to print() via QTimer::singleShot(0). } /** @@ -1218,9 +1218,29 @@ void ProjectPrintWindow::on_m_uncheck_all_clicked() void ProjectPrintWindow::print() { - m_preview->print(); + const bool isPdf = (m_printer->outputFormat() == QPrinter::PdfFormat); + const QString pdfFile = isPdf ? m_printer->outputFileName() : QString(); + + m_preview->print(); // triggers requestPaint() synchronously; painter + // is created/destroyed inside that call + savePageSetupForCurrentPrinter(); - this->close(); + + if (isPdf && !pdfFile.isEmpty()) { + // Defer post-processing and window close to the next event-loop + // iteration. This lets the macOS arm64 Metal compositor finish + // compositing the backing store before the window is destroyed, + // which prevents the black screen observed on Apple Silicon under + // macOS Sequoia (QPrintPreviewWidget + CALayer timing issue). + QTimer::singleShot(0, this, [this, pdfFile]() { + // Convert URI link annotations into native internal GoTo/FitR + // actions so cross-references jump inside the document. + pdfConvertUriToGoTo(pdfFile); + this->close(); + }); + } else { + this->close(); + } } void ProjectPrintWindow::on_m_date_cb_userDateChanged(const QDate &date)