gfx: survive, and configure, a screen with no window manager - #2183
Open
jcelerier wants to merge 7 commits into
Open
gfx: survive, and configure, a screen with no window manager#2183jcelerier wants to merge 7 commits into
jcelerier wants to merge 7 commits into
Conversation
This was referenced Aug 9, 2026
jcelerier
force-pushed
the
fix/eglfs-screen-placement
branch
2 times, most recently
from
August 10, 2026 05:05
17d941b to
809dde6
Compare
jcelerier
force-pushed
the
fix/eglfs-screen-placement
branch
from
August 11, 2026 18:26
809dde6 to
3d77a23
Compare
Without a window manager -- eglfs and friends, which is what score falls
back to on a board with no X and no Wayland -- a screen backs exactly one
native surface. Qt does not report the second one as an error: it calls
qFatal("EGLFS: OpenGL windows cannot be mixed with others.") from inside
QEglFSWindow::create(), so opening a gfx output while the editor is up
took the whole process down.
Measured on an rk3588 with two HDMI outputs (Qt 6.12): only the first
window on a screen gets a native surface, and every later one on that
screen must be raster -- widget-then-GL aborts, GL-then-widget composites
fine. setScreen() does not decide where a window lands, either: the
platform makes it fullscreen and rederives the screen from the geometry,
undoing setScreen() on its own. Setting the geometry to the target
screen's is what works.
So ask for a screen nobody holds, place by geometry, and when they are
all taken say so and leave the window unopened rather than aborting.
setRenderer has to tolerate a window that was never shown and therefore
has no render state.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wXVcUPzvcDTVzLpWJzeqP
On an appliance there is no xrandr and no display settings panel: the platform reads its configuration once, when it starts, and that is the only chance anyone gets to say what the outputs should do. Until now score had no way to say it, so an embedded board came up with whatever the driver happened to pick. score::gfx::DisplayConfig holds the outputs and what was asked of them, reads the real ones out of /sys/class/drm -- which answers before there is a QGuiApplication and while another process holds the display -- and turns the result into what each platform actually understands. It lives in score_lib_base rather than the gfx plug-in because it has to run from main() before QGuiApplication exists, long before plug-ins load. The two platforms are not equivalent and are not pretended to be. eglfs takes connector names, modes, formats, layout and cloning through its JSON. vkkhrdisplay has three integers -- physical device, display, mode -- addresses displays by index rather than name, and cannot be told about layout at all, so it gets its own settings instead of a translation that would be a guess. displayCapabilities() is what the UI asks, so the dialog offers less on vkkhrdisplay rather than lying, and says plainly on a desktop that none of it applies here. Only what was actually set is written: an absent key means the driver decides, which is a better default than anything score could invent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wXVcUPzvcDTVzLpWJzeqP
"Restart to take effect" was written into the feature, but it is not a property of the feature -- it is a property of the two Linux embedded backends, which read their configuration once when the platform plug-in starts. Windows and macOS can be told to change displays while running, through ChangeDisplaySettingsEx and CGDisplayConfiguration, and a change that can be applied live can also be undone, so those want a "keep this setting?" confirmation that eglfs can never offer. So requiresRestart becomes a capability rather than an assumption, and appliesToSystemDisplays marks the platforms where score *could* rearrange the machine's displays -- deliberately not doing so yet, and saying so, because doing it unasked on somebody's desktop would be hostile. Enumeration no longer returns nothing off Linux: with no /sys/class/drm it falls back to Qt's screen list, which knows less -- one mode, the current one -- but means the dialog lists real outputs on Windows and macOS instead of being empty. The fallback needs a QGuiApplication, which the settings UI has and startup does not, so startup still gets the kernel or nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wXVcUPzvcDTVzLpWJzeqP
A machine with no window manager can end up showing a render output and nothing else, either because that is what it is for or because a display setting was wrong and there is no desktop to fix it from. Until now there was no way back that did not involve another machine and a keyboard. The platform gives a screen to whichever window asks first and never takes it back, so the editor cannot be hidden and revealed: it has to not exist. editorUi says whether to build one at all, and Ctrl+Alt+Shift+E writes it back and starts score again, which is the only thing that can change the order. On vkkhrdisplay it also writes platformOverride, since that platform creates a window for a widget and then draws nothing into it -- the way back to an editor there is to come up under eglfs. resolvePlatform refuses to apply an override unless score was choosing an embedded platform anyway, so a file written for an appliance cannot hijack a desktop session that happens to read it. oneWindowPerScreen had grown a second definition once main() needed it before plug-ins load; it is now one function in score_lib_base, reading the platform when there is one and the environment when there is not. The graphics settings become two tabs rather than a page with a dialog hidden behind a button.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wXVcUPzvcDTVzLpWJzeqP
Qt for WebAssembly has no QProcess, and score builds there. Nothing that hands a screen to a single window exists on wasm either, so the relaunch is unreachable there -- but it still has to compile. The setting is written regardless, so a later launch honours it.
score_plugin_faust keeps faust's include directory private, so linking the plug-in does not hand the headers over. A distribution puts them where the compiler looks anyway and nobody noticed; an SDK build, which is how macOS is built, does not.
jcelerier
force-pushed
the
fix/eglfs-screen-placement
branch
from
August 18, 2026 01:08
3d77a23 to
3210a27
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Opening a gfx output window on a board with no X and no Wayland killed score outright.
setup_x11()falls back toeglfsthere, and eglfs gives a screen to whichever window asks first: the second one is not an error, it isqFatal("EGLFS: OpenGL windows cannot be mixed with others.")insideQEglFSWindow::create(), so the editor being up meant the output took the process down.Measured on an rk3588 with two HDMI outputs, Qt 6.12, one process per case:
setScreenonlyTwo findings, neither in the documentation and both confirmed against the Qt 6.12 sources:
QOpenGLCompositordraws them onto it — which is why the order matters.setScreen()does not place a window; its geometry does. The platform makes every window fullscreen and rederives the screen from where it landed.setGeometry(screen->geometry())beforeshow()is what works.So
ScreenNodeandMultiWindowNodeask for a screen nobody holds, place by geometry, and decline to open with a clear message when every screen is taken, instead of aborting.Configuring the displays
On an appliance there is no xrandr and no display panel: what the platform reads at startup is the only say anyone gets.
score::gfx::DisplayConfigholds the outputs and what was asked of them, reads the real ones from/sys/class/drm— which answers before there is aQGuiApplicationand while another process holds the display — and produces what each platform understands. It lives inscore_lib_basebecause it runs frommain()before plug-ins load.The platforms are not equivalent and are not pretended to be. eglfs takes connector names, modes, formats, layout and cloning through its JSON. vkkhrdisplay has three integers, addresses displays by index, and cannot be told about layout at all, so it gets its own settings rather than a guessed translation.
displayCapabilities()is what the UI asks, so the page offers less where less is possible, and says plainly on a desktop that nothing here applies to it.requiresRestartis a capability, not an assumption: Windows and macOS can change displays while running, and a change that can be applied live can be undone. Neither backend is implemented yet, which is whyperOutputConfigurationstays false there — the page must not offer what nothing behind it will do.Getting back to an editor
A machine can end up showing output and nothing else, by configuration or because a display setting was wrong. The platform never takes a screen back, so the editor cannot be hidden and revealed — it has to not exist.
editorUidecides whether one is built, and Ctrl+Alt+Shift+E writes the setting back and relaunches. On vkkhrdisplay it also setsplatformOverride, since that platform creates a window for a widget and then draws nothing into it.resolvePlatformrefuses an override unless score was choosing an embedded platform anyway, so an appliance's file cannot hijack a desktop session.Tests
test_unit_screen_placementandtest_unit_display_config: screen selection, sysfs enumeration against a fixture, KMS JSON generation, save/load round-trip, the capability split, and the platform-override rules. Every commit builds on its own, checked by building each in turn.