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

cups: add comprehensive CUPs setting page

- Add printers
- Delete printers
- Use polkit APIs as fallback on auth errors
- Fix ref system to conditionally subscribe to cups when wanted
This commit is contained in:
bbedward
2025-11-29 17:35:21 -05:00
parent df663aceb9
commit e6c3ae9397
31 changed files with 5993 additions and 558 deletions

View File

@@ -1,8 +1,6 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtCore
import QtQuick
import Quickshell
import Quickshell.Io
@@ -14,6 +12,7 @@ Singleton {
property bool dmsAvailable: false
property var capabilities: []
property int apiVersion: 0
property string cliVersion: ""
readonly property int expectedApiVersion: 1
property var availablePlugins: []
property var installedPlugins: []
@@ -57,18 +56,18 @@ Singleton {
Component.onCompleted: {
if (socketPath && socketPath.length > 0) {
detectUpdateCommand()
detectUpdateCommand();
}
}
function detectUpdateCommand() {
checkingUpdateCommand = true
checkAurHelper.running = true
checkingUpdateCommand = true;
checkAurHelper.running = true;
}
function startSocketConnection() {
if (socketPath && socketPath.length > 0) {
testProcess.running = true
testProcess.running = true;
}
}
@@ -79,26 +78,26 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
const helper = text.trim()
const helper = text.trim();
if (helper.includes("paru")) {
checkDmsPackage.helper = "paru"
checkDmsPackage.running = true
checkDmsPackage.helper = "paru";
checkDmsPackage.running = true;
} else if (helper.includes("yay")) {
checkDmsPackage.helper = "yay"
checkDmsPackage.running = true
checkDmsPackage.helper = "yay";
checkDmsPackage.running = true;
} else {
updateCommand = "dms update"
checkingUpdateCommand = false
startSocketConnection()
updateCommand = "dms update";
checkingUpdateCommand = false;
startSocketConnection();
}
}
}
onExited: exitCode => {
if (exitCode !== 0) {
updateCommand = "dms update"
checkingUpdateCommand = false
startSocketConnection()
updateCommand = "dms update";
checkingUpdateCommand = false;
startSocketConnection();
}
}
}
@@ -112,22 +111,22 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
if (text.includes("dms-shell-git")) {
updateCommand = checkDmsPackage.helper + " -S dms-shell-git"
updateCommand = checkDmsPackage.helper + " -S dms-shell-git";
} else if (text.includes("dms-shell-bin")) {
updateCommand = checkDmsPackage.helper + " -S dms-shell-bin"
updateCommand = checkDmsPackage.helper + " -S dms-shell-bin";
} else {
updateCommand = "dms update"
updateCommand = "dms update";
}
checkingUpdateCommand = false
startSocketConnection()
checkingUpdateCommand = false;
startSocketConnection();
}
}
onExited: exitCode => {
if (exitCode !== 0) {
updateCommand = "dms update"
checkingUpdateCommand = false
startSocketConnection()
updateCommand = "dms update";
checkingUpdateCommand = false;
startSocketConnection();
}
}
}
@@ -138,21 +137,21 @@ Singleton {
onExited: exitCode => {
if (exitCode === 0) {
root.dmsAvailable = true
connectSocket()
root.dmsAvailable = true;
connectSocket();
} else {
root.dmsAvailable = false
root.dmsAvailable = false;
}
}
}
function connectSocket() {
if (!dmsAvailable || isConnected || isConnecting) {
return
return;
}
isConnecting = true
requestSocket.connected = true
isConnecting = true;
requestSocket.connected = true;
}
DankSocket {
@@ -162,32 +161,32 @@ Singleton {
onConnectionStateChanged: {
if (connected) {
root.isConnected = true
root.isConnecting = false
root.connectionStateChanged()
subscribeSocket.connected = true
root.isConnected = true;
root.isConnecting = false;
root.connectionStateChanged();
subscribeSocket.connected = true;
} else {
root.isConnected = false
root.isConnecting = false
root.apiVersion = 0
root.capabilities = []
root.connectionStateChanged()
root.isConnected = false;
root.isConnecting = false;
root.apiVersion = 0;
root.capabilities = [];
root.connectionStateChanged();
}
}
parser: SplitParser {
onRead: line => {
if (!line || line.length === 0) {
return
return;
}
console.log("DMSService: Request socket <<", line)
console.log("DMSService: Request socket <<", line);
try {
const response = JSON.parse(line)
handleResponse(response)
const response = JSON.parse(line);
handleResponse(response);
} catch (e) {
console.warn("DMSService: Failed to parse request response:", line, e)
console.warn("DMSService: Failed to parse request response:", line, e);
}
}
}
@@ -199,25 +198,25 @@ Singleton {
connected: false
onConnectionStateChanged: {
root.subscribeConnected = connected
root.subscribeConnected = connected;
if (connected) {
sendSubscribeRequest()
sendSubscribeRequest();
}
}
parser: SplitParser {
onRead: line => {
if (!line || line.length === 0) {
return
return;
}
console.log("DMSService: Subscribe socket <<", line)
console.log("DMSService: Subscribe socket <<", line);
try {
const response = JSON.parse(line)
handleSubscriptionEvent(response)
const response = JSON.parse(line);
handleSubscriptionEvent(response);
} catch (e) {
console.warn("DMSService: Failed to parse subscription event:", line, e)
console.warn("DMSService: Failed to parse subscription event:", line, e);
}
}
}
@@ -226,319 +225,317 @@ Singleton {
function sendSubscribeRequest() {
const request = {
"method": "subscribe"
}
};
if (activeSubscriptions.length > 0) {
request.params = {
"services": activeSubscriptions
}
console.log("DMSService: Subscribing to services:", JSON.stringify(activeSubscriptions))
};
console.log("DMSService: Subscribing to services:", JSON.stringify(activeSubscriptions));
} else {
console.log("DMSService: Subscribing to all services")
console.log("DMSService: Subscribing to all services");
}
subscribeSocket.send(request)
subscribeSocket.send(request);
}
function subscribe(services) {
if (!Array.isArray(services)) {
services = [services]
services = [services];
}
activeSubscriptions = services
activeSubscriptions = services;
if (subscribeConnected) {
subscribeSocket.connected = false
subscribeSocket.connected = false;
Qt.callLater(() => {
subscribeSocket.connected = true
})
subscribeSocket.connected = true;
});
}
}
function addSubscription(service) {
if (activeSubscriptions.includes("all")) {
console.warn("DMSService: Cannot add specific subscription when subscribed to 'all'")
return
}
if (activeSubscriptions.includes("all"))
return;
if (!activeSubscriptions.includes(service)) {
const newSubs = [...activeSubscriptions, service]
subscribe(newSubs)
const newSubs = [...activeSubscriptions, service];
subscribe(newSubs);
}
}
function removeSubscription(service) {
if (activeSubscriptions.includes("all")) {
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "dwl", "brightness", "extworkspace"]
const filtered = allServices.filter(s => s !== service)
subscribe(filtered)
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "dwl", "brightness", "extworkspace"];
const filtered = allServices.filter(s => s !== service);
subscribe(filtered);
} else {
const filtered = activeSubscriptions.filter(s => s !== service)
const filtered = activeSubscriptions.filter(s => s !== service);
if (filtered.length === 0) {
console.warn("DMSService: Cannot remove last subscription")
return
console.warn("DMSService: Cannot remove last subscription");
return;
}
subscribe(filtered)
subscribe(filtered);
}
}
function subscribeAll() {
subscribe(["all"])
subscribe(["all"]);
}
function subscribeAllExcept(excludeServices) {
if (!Array.isArray(excludeServices)) {
excludeServices = [excludeServices]
excludeServices = [excludeServices];
}
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "cups", "dwl", "brightness", "extworkspace"]
const filtered = allServices.filter(s => !excludeServices.includes(s))
subscribe(filtered)
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "cups", "dwl", "brightness", "extworkspace"];
const filtered = allServices.filter(s => !excludeServices.includes(s));
subscribe(filtered);
}
function handleSubscriptionEvent(response) {
if (response.error) {
if (response.error.includes("unknown method") && response.error.includes("subscribe")) {
if (!shownOutdatedError) {
console.error("DMSService: Server does not support subscribe method")
ToastService.showError(I18n.tr("DMS out of date"), I18n.tr("To update, run the following command:"), updateCommand)
shownOutdatedError = true
console.error("DMSService: Server does not support subscribe method");
ToastService.showError(I18n.tr("DMS out of date"), I18n.tr("To update, run the following command:"), updateCommand);
shownOutdatedError = true;
}
}
return
return;
}
if (!response.result) {
return
return;
}
const service = response.result.service
const data = response.result.data
const service = response.result.service;
const data = response.result.data;
if (service === "server") {
apiVersion = data.apiVersion || 0
capabilities = data.capabilities || []
apiVersion = data.apiVersion || 0;
cliVersion = data.cliVersion || "";
capabilities = data.capabilities || [];
console.info("DMSService: Connected (API v" + apiVersion + ") -", JSON.stringify(capabilities))
console.info("DMSService: Connected (API v" + apiVersion + ", CLI " + cliVersion + ") -", JSON.stringify(capabilities));
if (apiVersion < expectedApiVersion) {
ToastService.showError("DMS server is outdated (API v" + apiVersion + ", expected v" + expectedApiVersion + ")")
ToastService.showError("DMS server is outdated (API v" + apiVersion + ", expected v" + expectedApiVersion + ")");
}
capabilitiesReceived()
capabilitiesReceived();
} else if (service === "network") {
networkStateUpdate(data)
networkStateUpdate(data);
} else if (service === "network.credentials") {
credentialsRequest(data)
credentialsRequest(data);
} else if (service === "loginctl") {
if (data.event) {
loginctlEvent(data)
loginctlEvent(data);
} else {
loginctlStateUpdate(data)
loginctlStateUpdate(data);
}
} else if (service === "bluetooth.pairing") {
bluetoothPairingRequest(data)
bluetoothPairingRequest(data);
} else if (service === "cups") {
cupsStateUpdate(data)
cupsStateUpdate(data);
} else if (service === "dwl") {
dwlStateUpdate(data)
dwlStateUpdate(data);
} else if (service === "brightness") {
brightnessStateUpdate(data)
brightnessStateUpdate(data);
} else if (service === "brightness.update") {
if (data.device) {
brightnessDeviceUpdate(data.device)
brightnessDeviceUpdate(data.device);
}
} else if (service === "extworkspace") {
extWorkspaceStateUpdate(data)
extWorkspaceStateUpdate(data);
} else if (service === "wlroutput") {
wlrOutputStateUpdate(data)
wlrOutputStateUpdate(data);
} else if (service === "evdev") {
if (data.capsLock !== undefined) {
capsLockState = data.capsLock
capsLockState = data.capsLock;
}
evdevStateUpdate(data)
evdevStateUpdate(data);
}
}
function sendRequest(method, params, callback) {
if (!isConnected) {
console.warn("DMSService.sendRequest: Not connected, method:", method)
console.warn("DMSService.sendRequest: Not connected, method:", method);
if (callback) {
callback({
"error": "not connected to DMS socket"
})
"error": "not connected to DMS socket"
});
}
return
return;
}
requestIdCounter++
const id = Date.now() + requestIdCounter
requestIdCounter++;
const id = Date.now() + requestIdCounter;
const request = {
"id": id,
"method": method
}
};
if (params) {
request.params = params
request.params = params;
}
if (callback) {
pendingRequests[id] = callback
pendingRequests[id] = callback;
}
console.log("DMSService.sendRequest: Sending request id=" + id + " method=" + method)
requestSocket.send(request)
console.log("DMSService.sendRequest: Sending request id=" + id + " method=" + method);
requestSocket.send(request);
}
function handleResponse(response) {
const callback = pendingRequests[response.id]
const callback = pendingRequests[response.id];
if (callback) {
delete pendingRequests[response.id]
callback(response)
delete pendingRequests[response.id];
callback(response);
}
}
function ping(callback) {
sendRequest("ping", null, callback)
sendRequest("ping", null, callback);
}
function listPlugins(callback) {
sendRequest("plugins.list", null, response => {
if (response.result) {
availablePlugins = response.result
pluginsListReceived(response.result)
}
if (callback) {
callback(response)
}
})
if (response.result) {
availablePlugins = response.result;
pluginsListReceived(response.result);
}
if (callback) {
callback(response);
}
});
}
function listInstalled(callback) {
sendRequest("plugins.listInstalled", null, response => {
if (response.result) {
installedPlugins = response.result
installedPluginsReceived(response.result)
}
if (callback) {
callback(response)
}
})
if (response.result) {
installedPlugins = response.result;
installedPluginsReceived(response.result);
}
if (callback) {
callback(response);
}
});
}
function search(query, category, compositor, capability, callback) {
const params = {
"query": query
}
};
if (category) {
params.category = category
params.category = category;
}
if (compositor) {
params.compositor = compositor
params.compositor = compositor;
}
if (capability) {
params.capability = capability
params.capability = capability;
}
sendRequest("plugins.search", params, response => {
if (response.result) {
searchResultsReceived(response.result)
}
if (callback) {
callback(response)
}
})
if (response.result) {
searchResultsReceived(response.result);
}
if (callback) {
callback(response);
}
});
}
function install(pluginName, callback) {
sendRequest("plugins.install", {
"name": pluginName
}, response => {
if (callback) {
callback(response)
}
if (!response.error) {
listInstalled()
}
})
"name": pluginName
}, response => {
if (callback) {
callback(response);
}
if (!response.error) {
listInstalled();
}
});
}
function uninstall(pluginName, callback) {
sendRequest("plugins.uninstall", {
"name": pluginName
}, response => {
if (callback) {
callback(response)
}
if (!response.error) {
listInstalled()
}
})
"name": pluginName
}, response => {
if (callback) {
callback(response);
}
if (!response.error) {
listInstalled();
}
});
}
function update(pluginName, callback) {
sendRequest("plugins.update", {
"name": pluginName
}, response => {
if (callback) {
callback(response)
}
if (!response.error) {
listInstalled()
}
})
"name": pluginName
}, response => {
if (callback) {
callback(response);
}
if (!response.error) {
listInstalled();
}
});
}
function lockSession(callback) {
sendRequest("loginctl.lock", null, callback)
sendRequest("loginctl.lock", null, callback);
}
function unlockSession(callback) {
sendRequest("loginctl.unlock", null, callback)
sendRequest("loginctl.unlock", null, callback);
}
function bluetoothPair(devicePath, callback) {
sendRequest("bluetooth.pair", {
"device": devicePath
}, callback)
"device": devicePath
}, callback);
}
function bluetoothConnect(devicePath, callback) {
sendRequest("bluetooth.connect", {
"device": devicePath
}, callback)
"device": devicePath
}, callback);
}
function bluetoothDisconnect(devicePath, callback) {
sendRequest("bluetooth.disconnect", {
"device": devicePath
}, callback)
"device": devicePath
}, callback);
}
function bluetoothRemove(devicePath, callback) {
sendRequest("bluetooth.remove", {
"device": devicePath
}, callback)
"device": devicePath
}, callback);
}
function bluetoothTrust(devicePath, callback) {
sendRequest("bluetooth.trust", {
"device": devicePath
}, callback)
"device": devicePath
}, callback);
}
function bluetoothSubmitPairing(token, secrets, accept, callback) {
sendRequest("bluetooth.pairing.submit", {
"token": token,
"secrets": secrets,
"accept": accept
}, callback)
"token": token,
"secrets": secrets,
"accept": accept
}, callback);
}
function bluetoothCancelPairing(token, callback) {
sendRequest("bluetooth.pairing.cancel", {
"token": token
}, callback)
"token": token
}, callback);
}
}