git: 1181ac708936 - main - vt: Reprogram display controller on KDSETMODE KD_TEXT for active window

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Fri, 18 Sep 2026 16:31:50 UTC
The branch main has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=1181ac708936d054e07089de7c5b3c1de11a4367

commit 1181ac708936d054e07089de7c5b3c1de11a4367
Author:     Quentin Thébault <quentin.thebault@defenso.fr>
AuthorDate: 2026-07-02 14:27:10 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2026-09-18 16:30:54 +0000

    vt: Reprogram display controller on KDSETMODE KD_TEXT for active window
    
    When a graphical application (X.Org display server, Wayland compositor)
    calls KDSETMODE to set the active VT back to KD_TEXT, vt(4) does not
    reset the CRTC to point to its framebuffer, leaving the last image of
    the graphical application visible instead of the text console until the
    next VT switch.
    
    Add a call to vd_postswitch to reset the CRTC, set VDF_INVALID to force
    a redraw and schedule the flush timer, like vt(4) does in
    vt_window_switch().
    
    Sponsored by: Defenso
    
    Signed-off-by: Quentin Thébault <quentin.thebault@defenso.fr>
    
    Reviewed by:    vexeduxr
    Pull request: https://github.com/freebsd/freebsd-src/pull/2308
---
 sys/dev/vt/vt_core.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/sys/dev/vt/vt_core.c b/sys/dev/vt/vt_core.c
index 81487d27a6fc..040b8ea52183 100644
--- a/sys/dev/vt/vt_core.c
+++ b/sys/dev/vt/vt_core.c
@@ -2923,9 +2923,31 @@ skip_thunk:
 		switch (*(int *)data) {
 		case KD_TEXT:
 		case KD_TEXT1:
-		case KD_PIXEL:
+		case KD_PIXEL: {
+			bool restore = false;
+			VT_LOCK(vd);
+			if ((vw->vw_flags & VWF_GRAPHICS) &&
+			    vw == vd->vd_curwindow) {
+				/*
+				 * When leaving graphics mode on the currently
+				 * active window, reprogram the display
+				 * controller back to text mode immediately.
+				 * Without this, the CRTC retains the graphical
+				 * state and the console stays invisible until
+				 * the next VT switch.
+				 */
+				restore = true;
+				vd->vd_flags |= VDF_INVALID;
+			}
+			VT_UNLOCK(vd);
+			if (restore) {
+				if (vd->vd_driver->vd_postswitch)
+					vd->vd_driver->vd_postswitch(vd);
+				vt_resume_flush_timer(vw, 0);
+			}
 			vw->vw_flags &= ~VWF_GRAPHICS;
 			break;
+		}
 		case KD_GRAPHICS:
 			vw->vw_flags |= VWF_GRAPHICS;
 			break;