1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

greeter: use folderlistmodel for session iteration, add launch timeout

This commit is contained in:
bbedward
2025-12-30 11:49:00 -05:00
parent 18f095cb23
commit 97a07c399a
3 changed files with 172 additions and 131 deletions

View File

@@ -1,11 +1,9 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtCore
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Common
Singleton { Singleton {
id: root id: root
@@ -20,40 +18,40 @@ Singleton {
property bool nightModeEnabled: false property bool nightModeEnabled: false
Component.onCompleted: { Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", greetCfgDir]) Quickshell.execDetached(["mkdir", "-p", greetCfgDir]);
loadMemory() loadMemory();
loadSessionConfig() loadSessionConfig();
} }
function loadMemory() { function loadMemory() {
parseMemory(memoryFileView.text()) parseMemory(memoryFileView.text());
} }
function loadSessionConfig() { function loadSessionConfig() {
parseSessionConfig(sessionConfigFileView.text()) parseSessionConfig(sessionConfigFileView.text());
} }
function parseSessionConfig(content) { function parseSessionConfig(content) {
try { try {
if (content && content.trim()) { if (content && content.trim()) {
const config = JSON.parse(content) const config = JSON.parse(content);
isLightMode = config.isLightMode !== undefined ? config.isLightMode : false isLightMode = config.isLightMode !== undefined ? config.isLightMode : false;
nightModeEnabled = config.nightModeEnabled !== undefined ? config.nightModeEnabled : false nightModeEnabled = config.nightModeEnabled !== undefined ? config.nightModeEnabled : false;
} }
} catch (e) { } catch (e) {
console.warn("Failed to parse greeter session config:", e) console.warn("Failed to parse greeter session config:", e);
} }
} }
function parseMemory(content) { function parseMemory(content) {
try { try {
if (content && content.trim()) { if (!content || !content.trim())
const memory = JSON.parse(content) return;
lastSessionId = memory.lastSessionId !== undefined ? memory.lastSessionId : "" const memory = JSON.parse(content);
lastSuccessfulUser = memory.lastSuccessfulUser !== undefined ? memory.lastSuccessfulUser : "" lastSessionId = memory.lastSessionId || "";
} lastSuccessfulUser = memory.lastSuccessfulUser || "";
} catch (e) { } catch (e) {
console.warn("Failed to parse greetd memory:", e) console.warn("Failed to parse greetd memory:", e);
} }
} }
@@ -61,17 +59,17 @@ Singleton {
memoryFileView.setText(JSON.stringify({ memoryFileView.setText(JSON.stringify({
"lastSessionId": lastSessionId, "lastSessionId": lastSessionId,
"lastSuccessfulUser": lastSuccessfulUser "lastSuccessfulUser": lastSuccessfulUser
}, null, 2)) }, null, 2));
} }
function setLastSessionId(id) { function setLastSessionId(id) {
lastSessionId = id || "" lastSessionId = id || "";
saveMemory() saveMemory();
} }
function setLastSuccessfulUser(username) { function setLastSuccessfulUser(username) {
lastSuccessfulUser = username || "" lastSuccessfulUser = username || "";
saveMemory() saveMemory();
} }
FileView { FileView {
@@ -83,7 +81,7 @@ Singleton {
watchChanges: false watchChanges: false
printErrors: false printErrors: false
onLoaded: { onLoaded: {
parseMemory(memoryFileView.text()) parseMemory(memoryFileView.text());
} }
} }
@@ -96,10 +94,10 @@ Singleton {
watchChanges: false watchChanges: false
printErrors: true printErrors: true
onLoaded: { onLoaded: {
parseSessionConfig(sessionConfigFileView.text()) parseSessionConfig(sessionConfigFileView.text());
} }
onLoadFailed: error => { onLoadFailed: error => {
console.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error) console.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error);
} }
} }
} }

View File

@@ -2,6 +2,7 @@ import QtCore
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Layouts import QtQuick.Layouts
import Qt.labs.folderlistmodel
import Quickshell import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import Quickshell.Io import Quickshell.Io
@@ -54,10 +55,8 @@ Item {
pickRandomFact(); pickRandomFact();
initWeatherService(); initWeatherService();
if (isPrimaryScreen) { if (isPrimaryScreen)
sessionListProc.running = true;
applyLastSuccessfulUser(); applyLastSuccessfulUser();
}
if (CompositorService.isHyprland) if (CompositorService.isHyprland)
updateHyprlandLayout(); updateHyprlandLayout();
@@ -1030,130 +1029,146 @@ Item {
alignPopupRight: true alignPopupRight: true
onValueChanged: value => { onValueChanged: value => {
const idx = GreeterState.sessionList.indexOf(value); const idx = GreeterState.sessionList.indexOf(value);
if (idx >= 0) { if (idx < 0)
GreeterState.currentSessionIndex = idx; return;
GreeterState.selectedSession = GreeterState.sessionExecs[idx]; GreeterState.currentSessionIndex = idx;
GreetdMemory.setLastSessionId(GreeterState.sessionPaths[idx]); GreeterState.selectedSession = GreeterState.sessionExecs[idx];
} GreeterState.selectedSessionPath = GreeterState.sessionPaths[idx];
} }
} }
} }
} }
property string currentSessionName: GreeterState.sessionList[GreeterState.currentSessionIndex] || "" property string currentSessionName: GreeterState.sessionList[GreeterState.currentSessionIndex] || ""
property int pendingParsers: 0
function finalizeSessionSelection() { function finalizeSessionSelection() {
if (GreeterState.sessionList.length === 0) { if (GreeterState.sessionList.length === 0)
return; return;
}
const savedSession = GreetdMemory.lastSessionId; const savedSession = GreetdMemory.lastSessionId;
let foundSaved = false;
if (savedSession) { if (savedSession) {
for (var i = 0; i < GreeterState.sessionPaths.length; i++) { for (var i = 0; i < GreeterState.sessionPaths.length; i++) {
if (GreeterState.sessionPaths[i] === savedSession) { if (GreeterState.sessionPaths[i] === savedSession) {
GreeterState.currentSessionIndex = i; GreeterState.currentSessionIndex = i;
foundSaved = true; GreeterState.selectedSession = GreeterState.sessionExecs[i] || "";
break; GreeterState.selectedSessionPath = GreeterState.sessionPaths[i];
return;
} }
} }
} }
if (!foundSaved) { GreeterState.currentSessionIndex = 0;
GreeterState.currentSessionIndex = 0; GreeterState.selectedSession = GreeterState.sessionExecs[0] || "";
} GreeterState.selectedSessionPath = GreeterState.sessionPaths[0] || "";
GreeterState.selectedSession = GreeterState.sessionExecs[GreeterState.currentSessionIndex] || GreeterState.sessionExecs[0] || "";
} }
Process { property var sessionDirs: {
id: sessionListProc const homeDir = Quickshell.env("HOME") || "";
property string homeDir: Quickshell.env("HOME") || "" const dirs = ["/usr/share/wayland-sessions", "/usr/share/xsessions", "/usr/local/share/wayland-sessions", "/usr/local/share/xsessions"];
property string xdgDirs: xdgDataDirs || ""
command: {
var paths = ["/usr/share/wayland-sessions", "/usr/share/xsessions", "/usr/local/share/wayland-sessions", "/usr/local/share/xsessions"];
if (homeDir) {
paths.push(homeDir + "/.local/share/wayland-sessions");
paths.push(homeDir + "/.local/share/xsessions");
}
// Add XDG_DATA_DIRS paths
if (xdgDirs) {
xdgDirs.split(":").forEach(function (dir) {
if (dir) {
paths.push(dir + "/wayland-sessions");
paths.push(dir + "/xsessions");
}
});
}
// 1. Explicit system/user paths
var explicitFind = "find " + paths.join(" ") + " -maxdepth 1 -name '*.desktop' -type f -follow 2>/dev/null";
// 2. Scan all /home user directories for local session files
var homeScan = "find /home -maxdepth 5 \\( -path '*/wayland-sessions/*.desktop' -o -path '*/xsessions/*.desktop' \\) -type f -follow 2>/dev/null";
var findCmd = "(" + explicitFind + "; " + homeScan + ") | sort -u";
return ["sh", "-c", findCmd];
}
running: false
stdout: SplitParser { if (homeDir) {
onRead: data => { dirs.push(homeDir + "/.local/share/wayland-sessions");
if (data.trim()) { dirs.push(homeDir + "/.local/share/xsessions");
root.pendingParsers++; }
parseDesktopFile(data.trim());
if (xdgDataDirs) {
xdgDataDirs.split(":").forEach(dir => {
if (dir) {
dirs.push(dir + "/wayland-sessions");
dirs.push(dir + "/xsessions");
} }
} });
} }
return dirs;
} }
function parseDesktopFile(path) { property var _pendingFiles: ({})
const parser = desktopParser.createObject(null, { property int _pendingCount: 0
"desktopPath": path
function _addSession(path, name, exec) {
if (!name || !exec || GreeterState.sessionList.includes(name))
return;
GreeterState.sessionList = GreeterState.sessionList.concat([name]);
GreeterState.sessionExecs = GreeterState.sessionExecs.concat([exec]);
GreeterState.sessionPaths = GreeterState.sessionPaths.concat([path]);
}
function _parseDesktopFile(content, path) {
let name = "";
let exec = "";
const lines = content.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (!name && line.startsWith("Name="))
name = line.substring(5).trim();
else if (!exec && line.startsWith("Exec="))
exec = line.substring(5).trim();
if (name && exec)
break;
}
_addSession(path, name, exec);
}
function _loadDesktopFile(filePath) {
if (_pendingFiles[filePath])
return;
_pendingFiles[filePath] = true;
_pendingCount++;
const loader = desktopFileLoader.createObject(root, {
"filePath": filePath
}); });
} }
function _onFileLoaded(filePath) {
_pendingCount--;
if (_pendingCount === 0)
Qt.callLater(finalizeSessionSelection);
}
Component { Component {
id: desktopParser id: desktopFileLoader
Process {
property string desktopPath: ""
command: ["bash", "-c", `grep -E '^(Name|Exec)=' "${desktopPath}"`]
running: true
stdout: StdioCollector { FileView {
onStreamFinished: { id: fv
const lines = text.split("\n"); property string filePath: ""
let name = ""; path: filePath
let exec = "";
for (const line of lines) { onLoaded: {
if (line.startsWith("Name=")) { root._parseDesktopFile(text(), filePath);
name = line.substring(5).trim(); root._onFileLoaded(filePath);
} else if (line.startsWith("Exec=")) { fv.destroy();
exec = line.substring(5).trim();
}
}
if (name && exec) {
if (!GreeterState.sessionList.includes(name)) {
let newList = GreeterState.sessionList.slice();
let newExecs = GreeterState.sessionExecs.slice();
let newPaths = GreeterState.sessionPaths.slice();
newList.push(name);
newExecs.push(exec);
newPaths.push(desktopPath);
GreeterState.sessionList = newList;
GreeterState.sessionExecs = newExecs;
GreeterState.sessionPaths = newPaths;
}
}
}
} }
onExited: code => { onLoadFailed: {
root.pendingParsers--; root._onFileLoaded(filePath);
if (root.pendingParsers === 0) { fv.destroy();
Qt.callLater(root.finalizeSessionSelection); }
}
}
Repeater {
model: isPrimaryScreen ? sessionDirs : []
Item {
required property string modelData
FolderListModel {
folder: "file://" + modelData
nameFilters: ["*.desktop"]
showDirs: false
showDotAndDotDot: false
onStatusChanged: {
if (status !== FolderListModel.Ready)
return;
for (let i = 0; i < count; i++) {
let fp = get(i, "filePath");
if (fp.startsWith("file://"))
fp = fp.substring(7);
root._loadDesktopFile(fp);
}
} }
destroy();
} }
} }
} }
@@ -1167,22 +1182,31 @@ Item {
Greetd.respond(GreeterState.passwordBuffer); Greetd.respond(GreeterState.passwordBuffer);
GreeterState.passwordBuffer = ""; GreeterState.passwordBuffer = "";
inputField.text = ""; inputField.text = "";
} else if (!error) { return;
Greetd.respond("");
} }
if (!error)
Greetd.respond("");
} }
function onReadyToLaunch() { function onReadyToLaunch() {
GreeterState.unlocking = true;
const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex]; const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex];
if (sessionCmd) { const sessionPath = GreeterState.selectedSessionPath || GreeterState.sessionPaths[GreeterState.currentSessionIndex];
GreetdMemory.setLastSessionId(GreeterState.sessionPaths[GreeterState.currentSessionIndex]); if (!sessionCmd) {
GreetdMemory.setLastSuccessfulUser(GreeterState.username); GreeterState.pamState = "error";
Greetd.launch(sessionCmd.split(" "), ["XDG_SESSION_TYPE=wayland"]); placeholderDelay.restart();
return;
} }
GreeterState.unlocking = true;
launchTimeout.restart();
GreetdMemory.setLastSessionId(sessionPath);
GreetdMemory.setLastSuccessfulUser(GreeterState.username);
Greetd.launch(sessionCmd.split(" "), ["XDG_SESSION_TYPE=wayland"]);
} }
function onAuthFailure(message) { function onAuthFailure(message) {
launchTimeout.stop();
GreeterState.unlocking = false;
GreeterState.pamState = "fail"; GreeterState.pamState = "fail";
GreeterState.passwordBuffer = ""; GreeterState.passwordBuffer = "";
inputField.text = ""; inputField.text = "";
@@ -1190,8 +1214,26 @@ Item {
} }
function onError(error) { function onError(error) {
launchTimeout.stop();
GreeterState.unlocking = false;
GreeterState.pamState = "error";
GreeterState.passwordBuffer = "";
inputField.text = "";
placeholderDelay.restart();
Greetd.cancelSession();
}
}
Timer {
id: launchTimeout
interval: 8000
onTriggered: {
if (!GreeterState.unlocking)
return;
GreeterState.unlocking = false;
GreeterState.pamState = "error"; GreeterState.pamState = "error";
placeholderDelay.restart(); placeholderDelay.restart();
Greetd.cancelSession();
} }
} }

View File

@@ -1,7 +1,7 @@
import QtQuick
import Quickshell
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Singleton { Singleton {
id: root id: root
@@ -11,6 +11,7 @@ Singleton {
property string usernameInput: "" property string usernameInput: ""
property bool showPasswordInput: false property bool showPasswordInput: false
property string selectedSession: "" property string selectedSession: ""
property string selectedSessionPath: ""
property string pamState: "" property string pamState: ""
property bool unlocking: false property bool unlocking: false
@@ -20,10 +21,10 @@ Singleton {
property int currentSessionIndex: 0 property int currentSessionIndex: 0
function reset() { function reset() {
showPasswordInput = false showPasswordInput = false;
username = "" username = "";
usernameInput = "" usernameInput = "";
passwordBuffer = "" passwordBuffer = "";
pamState = "" pamState = "";
} }
} }