mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-12 23:32:50 -04:00
273 lines
6.4 KiB
Markdown
273 lines
6.4 KiB
Markdown
# Daemon Plugin Guide
|
|
|
|
Daemon plugins are invisible background services that react to events and execute actions. They have no bar pills or desktop presence.
|
|
|
|
## Base Component
|
|
|
|
Daemons use `PluginComponent` with no bar pills:
|
|
|
|
```qml
|
|
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Modules.Plugins
|
|
|
|
PluginComponent {
|
|
id: root
|
|
property var popoutService: null
|
|
|
|
// Event-driven logic goes here
|
|
}
|
|
```
|
|
|
|
## When to Use Daemons
|
|
|
|
- Monitor system events (wallpaper changes, battery level, notifications)
|
|
- Run periodic background tasks (polling APIs, checking system state)
|
|
- Execute scripts in response to events
|
|
- Control shell UI via PopoutService based on conditions
|
|
|
|
## Event-Driven Pattern
|
|
|
|
Use `Connections` to react to service signals:
|
|
|
|
```qml
|
|
PluginComponent {
|
|
property var popoutService: null
|
|
|
|
Connections {
|
|
target: SessionData
|
|
function onWallpaperPathChanged() {
|
|
console.log("Wallpaper changed to:", SessionData.wallpaperPath)
|
|
runScript(SessionData.wallpaperPath)
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: BatteryService
|
|
function onPercentageChanged() {
|
|
if (BatteryService.percentage < 10 && !BatteryService.isCharging) {
|
|
popoutService?.openBattery()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Available Services
|
|
|
|
Common services daemons can connect to:
|
|
|
|
| Service | Signals/Properties | Description |
|
|
|---------|-------------------|-------------|
|
|
| `SessionData` | `wallpaperPath`, `onWallpaperPathChanged` | Desktop session state |
|
|
| `BatteryService` | `percentage`, `isCharging`, `batteryAvailable` | Battery status |
|
|
| `NotificationService` | `onNotificationReceived(notification)` | Desktop notifications |
|
|
| `PluginService` | `onPluginLoaded`, `onGlobalVarChanged` | Plugin lifecycle |
|
|
|
|
Import services from `qs.Services`.
|
|
|
|
## Process Execution
|
|
|
|
### Simple command with Proc
|
|
|
|
```qml
|
|
import qs.Common
|
|
|
|
PluginComponent {
|
|
function runScript(arg) {
|
|
Proc.runCommand(
|
|
"myDaemon.script",
|
|
["bash", "-c", "echo 'Processing: " + arg + "'"],
|
|
(stdout, exitCode) => {
|
|
if (exitCode === 0) {
|
|
console.log("Script output:", stdout)
|
|
} else {
|
|
ToastService?.showInfo("Script failed: exit " + exitCode)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Long-running process with Process component
|
|
|
|
```qml
|
|
import Quickshell.Io
|
|
|
|
PluginComponent {
|
|
property string scriptPath: ""
|
|
|
|
Process {
|
|
id: proc
|
|
command: ["bash", scriptPath]
|
|
running: false
|
|
|
|
stdout: StdioCollector {
|
|
onTextReceived: (text) => {
|
|
console.log("stdout:", text)
|
|
}
|
|
}
|
|
|
|
stderr: StdioCollector {
|
|
onTextReceived: (text) => {
|
|
console.error("stderr:", text)
|
|
}
|
|
}
|
|
|
|
onExited: (exitCode) => {
|
|
if (exitCode !== 0) {
|
|
ToastService?.showInfo("Process failed: exit " + exitCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
function startProcess() {
|
|
if (scriptPath && !proc.running) {
|
|
proc.running = true
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Timer-Based Polling
|
|
|
|
```qml
|
|
PluginComponent {
|
|
Timer {
|
|
interval: 60000 // every minute
|
|
running: true
|
|
repeat: true
|
|
onTriggered: checkStatus()
|
|
}
|
|
|
|
function checkStatus() {
|
|
Proc.runCommand(
|
|
"myDaemon.check",
|
|
["sh", "-c", "systemctl is-active myservice"],
|
|
(stdout, exitCode) => {
|
|
const active = stdout.trim() === "active"
|
|
PluginService.setGlobalVar("myDaemon", "serviceActive", active)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Data Persistence
|
|
|
|
Daemons access PluginService directly (it's injected via PluginComponent):
|
|
|
|
```qml
|
|
PluginComponent {
|
|
property string configuredScript: pluginData?.scriptPath || ""
|
|
|
|
Connections {
|
|
target: pluginService
|
|
function onPluginDataChanged(changedId) {
|
|
if (changedId === pluginId) {
|
|
configuredScript = pluginService.loadPluginData(pluginId, "scriptPath", "")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## PopoutService Usage
|
|
|
|
Daemons can control shell UI via the injected popoutService:
|
|
|
|
```qml
|
|
PluginComponent {
|
|
property var popoutService: null
|
|
|
|
function showAlert() {
|
|
popoutService?.openNotificationCenter()
|
|
}
|
|
|
|
function openSettings() {
|
|
popoutService?.openSettings()
|
|
}
|
|
}
|
|
```
|
|
|
|
See [popout-service-reference.md](popout-service-reference.md) for the full API.
|
|
|
|
## Complete Example
|
|
|
|
Based on the WallpaperWatcherDaemon:
|
|
|
|
```qml
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Modules.Plugins
|
|
|
|
PluginComponent {
|
|
id: root
|
|
property var popoutService: null
|
|
|
|
property string scriptPath: pluginData?.scriptPath || ""
|
|
|
|
Connections {
|
|
target: pluginService
|
|
function onPluginDataChanged(changedId) {
|
|
if (changedId === pluginId) {
|
|
scriptPath = pluginService.loadPluginData(pluginId, "scriptPath", "")
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: SessionData
|
|
function onWallpaperPathChanged() {
|
|
if (scriptPath) {
|
|
runWallpaperScript(SessionData.wallpaperPath)
|
|
}
|
|
}
|
|
}
|
|
|
|
function runWallpaperScript(wallpaperPath) {
|
|
console.log("[WallpaperWatcher] Running script:", scriptPath, wallpaperPath)
|
|
|
|
Proc.runCommand(
|
|
"wallpaperWatcher.run",
|
|
["bash", scriptPath, wallpaperPath],
|
|
(stdout, exitCode) => {
|
|
if (exitCode === 0) {
|
|
console.log("[WallpaperWatcher] Script output:", stdout)
|
|
} else {
|
|
console.error("[WallpaperWatcher] Script failed:", exitCode)
|
|
ToastService?.showInfo("Wallpaper script failed")
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
console.log("[WallpaperWatcher] Daemon started")
|
|
}
|
|
}
|
|
```
|
|
|
|
## Manifest Example
|
|
|
|
```json
|
|
{
|
|
"id": "wallpaperWatcher",
|
|
"name": "Wallpaper Watcher",
|
|
"description": "Runs a script when the wallpaper changes",
|
|
"version": "1.0.0",
|
|
"author": "Developer",
|
|
"type": "daemon",
|
|
"capabilities": ["wallpaper-automation"],
|
|
"component": "./WallpaperWatcher.qml",
|
|
"icon": "wallpaper",
|
|
"settings": "./Settings.qml",
|
|
"permissions": ["settings_read", "settings_write", "process"]
|
|
}
|
|
```
|