add noctalia and fuzzel
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
pragma Singleton
|
||||
import Qt.labs.folderlistmodel 2.10
|
||||
import QtQml.Models
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Component registration - only poll when something needs lock key state
|
||||
function registerComponent(componentId) {
|
||||
root._registered[componentId] = true;
|
||||
root._registered = Object.assign({}, root._registered);
|
||||
Logger.d("LockKeys", "Component registered:", componentId, "- total:", root._registeredCount);
|
||||
}
|
||||
|
||||
function unregisterComponent(componentId) {
|
||||
delete root._registered[componentId];
|
||||
root._registered = Object.assign({}, root._registered);
|
||||
Logger.d("LockKeys", "Component unregistered:", componentId, "- total:", root._registeredCount);
|
||||
}
|
||||
|
||||
property var _registered: ({})
|
||||
readonly property int _registeredCount: Object.keys(_registered).length
|
||||
readonly property bool shouldRun: _registeredCount > 0
|
||||
|
||||
property bool capsLockOn: false
|
||||
property bool numLockOn: false
|
||||
property bool scrollLockOn: false
|
||||
|
||||
signal capsLockChanged(bool active)
|
||||
signal numLockChanged(bool active)
|
||||
signal scrollLockChanged(bool active)
|
||||
|
||||
Instantiator {
|
||||
model: FolderListModel {
|
||||
id: folderModel
|
||||
folder: Qt.resolvedUrl("/sys/class/leds")
|
||||
showFiles: false
|
||||
showOnlyReadable: true
|
||||
}
|
||||
delegate: Component {
|
||||
FileView {
|
||||
id: fileView
|
||||
path: filePath + "/brightness"
|
||||
// sysfs brightness can fail transiently (e.g. resume); omit console spam like other sysfs FileViews.
|
||||
printErrors: false
|
||||
watchChanges: false
|
||||
|
||||
function parseBrightnessLedOn(raw) {
|
||||
var t = raw.trim();
|
||||
if (t === "" || !/^[0-9]+$/.test(t))
|
||||
return null;
|
||||
return parseInt(t, 10) !== 0;
|
||||
}
|
||||
|
||||
function applyLockState(kind, state, emitIfChanged) {
|
||||
switch (kind) {
|
||||
case "numlock":
|
||||
if (emitIfChanged && root.numLockOn !== state) {
|
||||
root.numLockOn = state;
|
||||
root.numLockChanged(state);
|
||||
Logger.i("LockKeysService", "Num Lock:", state, fileView.path);
|
||||
} else if (!emitIfChanged) {
|
||||
root.numLockOn = state;
|
||||
}
|
||||
break;
|
||||
case "capslock":
|
||||
if (emitIfChanged && root.capsLockOn !== state) {
|
||||
root.capsLockOn = state;
|
||||
root.capsLockChanged(state);
|
||||
Logger.i("LockKeysService", "Caps Lock:", state, fileView.path);
|
||||
} else if (!emitIfChanged) {
|
||||
root.capsLockOn = state;
|
||||
}
|
||||
break;
|
||||
case "scrolllock":
|
||||
if (emitIfChanged && root.scrollLockOn !== state) {
|
||||
root.scrollLockOn = state;
|
||||
root.scrollLockChanged(state);
|
||||
Logger.i("LockKeysService", "Scroll Lock:", state, fileView.path);
|
||||
} else if (!emitIfChanged) {
|
||||
root.scrollLockOn = state;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only apply after a successful read — failed reloads must not update UI (empty text is not "off").
|
||||
onLoaded: {
|
||||
if (!fileView.isWanted)
|
||||
return;
|
||||
var state = fileView.parseBrightnessLedOn(fileView.text());
|
||||
if (state === null)
|
||||
return;
|
||||
|
||||
var kind = fileName.split("::")[1];
|
||||
|
||||
// First read after polling starts: sync bar/UI from sysfs without firing
|
||||
// *Changed signals (OSD listens to those and would flash on startup).
|
||||
if (!fileView.initialCheckDone) {
|
||||
fileView.initialCheckDone = true;
|
||||
fileView.applyLockState(kind, state, false);
|
||||
return;
|
||||
}
|
||||
|
||||
fileView.applyLockState(kind, state, true);
|
||||
}
|
||||
|
||||
// FolderListModel only provides filters for file names, not folders
|
||||
property bool isWanted: {
|
||||
if (fileName.startsWith("input") && fileName.includes("::")) {
|
||||
switch (fileName.split("::")[1]) {
|
||||
case "numlock":
|
||||
case "capslock":
|
||||
case "scrolllock":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// After shouldRun becomes true, first brightness read updates properties only (no *Changed signals).
|
||||
property bool initialCheckDone: false
|
||||
property variant connections: Connections {
|
||||
target: root
|
||||
function onShouldRunChanged() {
|
||||
if (root.shouldRun) {
|
||||
fileView.initialCheckDone = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sysfs does not provide change notifications
|
||||
property variant refreshTimer: Timer {
|
||||
interval: 200
|
||||
running: root.shouldRun && fileView.isWanted
|
||||
repeat: true
|
||||
onTriggered: fileView.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.i("LockKeysService", "Service started (polling deferred until a consumer registers).");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user