qt/opengl: paint transparent when subsurface holds the live frame
On the OpenGL Wayland subsurface path (EglDmabufTarget available),
renderTerminal blits m_fbo -> m_eglTarget and hands the dmabuf to
the SubsurfacePresenter; it does NOT update m_image (the QImage
fallback). But paintEvent's OpenGL branch unconditionally did
`painter.drawImage(QPointF(0,0), m_image)` over the parent QWidget,
overwriting the otherwise-transparent backing store. Since the
subsurface is stacked BELOW the parent via place_below, the stale
m_image painted on top obscured the live subsurface — visually:
a "weird overlay" with the real terminal pixels ghosting through
beneath it.
Two-part fix mirroring the Vulkan branch:
- renderTerminal: set m_subsurfaceHasFrame=true after a successful
presentDmabuf. Same flag the drainVulkan path flips.
- paintEvent OpenGL branch:
- subsurfaceActive && hasFrame -> fill transparent (let subsurface
show through)
- subsurfaceActive && !hasFrame -> fill configured `background` color
(placeholder during bring-up / post-resize gap)
- !subsurfaceActive -> legacy m_image blit (presenter
absent or EglDmabufTarget::create failed; renderer fell back to
glReadPixels into m_image)
Only affects the OpenGL variant; the Vulkan variant preprocesses the
entire OpenGL branch out.
pull/12846/head
parent
e9793b10df
commit
e21bc6b2b0
|
|
@ -903,6 +903,14 @@ void GhosttySurface::renderTerminal() {
|
|||
// (new buffer, new position, new dest, hide()) never applies
|
||||
// and the GL pane shows stale / black / ghosted content.
|
||||
forceParentCommit();
|
||||
// Flip the "real subsurface frame attached" flag so paintEvent
|
||||
// stops drawing m_image over the subsurface. Without this,
|
||||
// paintEvent unconditionally blits stale m_image content onto
|
||||
// the parent QWidget (which is stacked ABOVE the subsurface
|
||||
// via place_below), and the user sees the m_image as a weird
|
||||
// overlay with the real subsurface terminal pixels "ghosting"
|
||||
// through. Mirrors the Vulkan drainVulkan path's flag set.
|
||||
m_subsurfaceHasFrame.store(true, std::memory_order_release);
|
||||
// The terminal pixels reach the compositor via the subsurface,
|
||||
// not via QPainter — but chrome (overlays, dim, bell flash)
|
||||
// still goes through paintEvent. update() schedules that.
|
||||
|
|
@ -976,16 +984,43 @@ void GhosttySurface::paintEvent(QPaintEvent *) {
|
|||
}
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
} else {
|
||||
// Blit the framebuffer 1:1. m_image carries the device pixel ratio, so
|
||||
// the QPointF overload draws it at its true logical size. When in
|
||||
// sync that exactly fills the widget; mid-resize, the previous frame
|
||||
// stays at its real size in the top-left corner (rather than being
|
||||
// stretched to the new widget rect, which the user dislikes more
|
||||
// than the transient gap).
|
||||
// CompositionMode_Source replaces the transparent widget pixels with
|
||||
// the terminal image, alpha included, so its translucency is kept.
|
||||
// OpenGL path. When the subsurface presenter is active and has
|
||||
// a real frame attached, the terminal pixels reach the
|
||||
// compositor via the wl_subsurface (stacked BELOW the parent
|
||||
// QWidget via place_below). We must paint the parent's
|
||||
// terminal area transparent so the subsurface shows through —
|
||||
// mirroring the Vulkan branch above. Drawing m_image here
|
||||
// overwrites the transparent backing store with whatever
|
||||
// m_image last held (which is stale, because the subsurface
|
||||
// path in renderTerminal SKIPS the `m_image = m_fbo->toImage()`
|
||||
// readback), and the result is a "ghost overlay" effect where
|
||||
// the user sees m_image stacked above the live subsurface
|
||||
// pixels.
|
||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
painter.drawImage(QPointF(0, 0), m_image);
|
||||
if (subsurfaceActive && subsurfaceHasFrame) {
|
||||
painter.fillRect(rect(), Qt::transparent);
|
||||
} else if (subsurfaceActive) {
|
||||
// Subsurface presenter is up but the first real frame hasn't
|
||||
// attached yet (new-tab bring-up or post-resize gap). Paint
|
||||
// the terminal's configured background color over the
|
||||
// transparent parent so the user sees an empty terminal
|
||||
// rather than a transparent flash. Same placeholder logic as
|
||||
// the Vulkan branch.
|
||||
QColor fill = QColor(0, 0, 0); // safe fallback
|
||||
ghostty_config_color_s bg{};
|
||||
if (config::get(&bg, "background")) {
|
||||
fill = QColor(bg.r, bg.g, bg.b);
|
||||
}
|
||||
painter.fillRect(rect(), fill);
|
||||
} else {
|
||||
// Legacy QImage fallback path — the subsurface presenter is
|
||||
// absent (compositor refused linux-dmabuf-v1 or
|
||||
// EglDmabufTarget::create failed) and the renderer fell
|
||||
// back to glReadPixels into m_image. Blit it 1:1. m_image
|
||||
// carries the device pixel ratio, so the QPointF overload
|
||||
// draws it at its true logical size.
|
||||
painter.drawImage(QPointF(0, 0), m_image);
|
||||
}
|
||||
}
|
||||
|
||||
// Unfocused-split dimming: a translucent fill over an inactive pane.
|
||||
|
|
|
|||
Loading…
Reference in New Issue