// SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2026 Danilo M. // // UDT SDDM greeter. SDDM creates one window per screen, each sized to its own // screen and carrying a one-row ScreenModel, so this root item is exactly one // screen. The wallpaper fills it; the login panel draws only on the screen // named by config uiScreen, else on the primary screen. Input wakes the panel // after it fades. import QtQuick import "components" Item { id: root // SDDM creates one window per screen and sizes it to that screen. The // model holds exactly one row and geometry(index) ignores index, returning // this window's own screen at origin (0,0). readonly property var windowGeometry: screenModel.geometry(0) width: windowGeometry.width height: windowGeometry.height // Palette. Values come from theme.conf, overridden live by theme.conf.user. readonly property string baseColor: config.stringValue("base") readonly property string wallpaperUrl: config.stringValue("background") === "" ? "" : "file://" + config.stringValue("background") // The screen the panel belongs on: config uiScreen if non-empty, else the // primary screen. `primaryScreen` is a per-window context property SDDM // sets true only on the window showing the primary screen. readonly property string wantedScreen: config.stringValue("uiScreen") property string thisScreenName: "" readonly property bool cardScreen: wantedScreen === "" ? primaryScreen : (thisScreenName === wantedScreen) // The wallpaper fills this window's single screen. The frosted panel // samples this image. Image { id: wallpaperImage anchors.fill: parent source: root.wallpaperUrl fillMode: Image.PreserveAspectCrop sourceSize.width: width sourceSize.height: height smooth: true asynchronous: true } // Scrim so the panel and text read on any wallpaper. Rectangle { anchors.fill: parent color: root.baseColor opacity: 0.45 } // Non-visual: reads the model's single row to learn this screen's name. Repeater { model: screenModel delegate: Item { visible: false Component.onCompleted: root.thisScreenName = name } } // Idle: fade the panel after fadeoutMs, restore on any input. property bool uiVisible: true // While faded the panel is disabled, which clears its focus; take focus on // the root so the first key press wakes the panel instead of vanishing. onUiVisibleChanged: { if (!uiVisible) root.forceActiveFocus() } Timer { id: idle interval: config.intValue("fadeoutMs") > 0 ? config.intValue("fadeoutMs") : 60000 running: root.uiVisible repeat: false onTriggered: root.uiVisible = false } function wake() { if (!root.uiVisible) { root.uiVisible = true panel.restoreFocus() } idle.restart() } MouseArea { anchors.fill: parent hoverEnabled: true onPressed: root.wake() onPositionChanged: root.wake() } Keys.onPressed: event => { root.wake() event.accepted = false } // Panel width as a fraction of the window, with a sane fallback when the // key is missing or malformed. readonly property real panelWidth: { var v = parseFloat(config.stringValue("panelWidth")) return v > 0 ? Math.min(v, 1) : 0.30 } Panel { id: panel anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom width: parent.width * root.panelWidth visible: root.cardScreen enabled: root.uiVisible opacity: root.uiVisible ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } } backdropSource: wallpaperImage onWake: root.wake() } }