feat: kde wayland support #57

This commit is contained in:
leukipp
2025-08-26 17:15:21 +02:00
parent 619179c3c8
commit ad22dd3215
2 changed files with 127 additions and 53 deletions
+108 -44
View File
@@ -17,12 +17,13 @@ global.HARDWARE = global.HARDWARE || {
}, },
display: { display: {
status: { status: {
path: null,
value: null, value: null,
path: null,
command: null,
}, },
brightness: { brightness: {
path: null,
value: null, value: null,
path: null,
max: null, max: null,
}, },
}, },
@@ -37,7 +38,7 @@ global.HARDWARE = global.HARDWARE || {
* @returns {bool} Returns true if the initialization was successful. * @returns {bool} Returns true if the initialization was successful.
*/ */
const init = async () => { const init = async () => {
if (!compatibleDevice()) { if (!compatibleSystem()) {
console.warn("Operating system is not supported\n"); console.warn("Operating system is not supported\n");
return false; return false;
} }
@@ -49,14 +50,13 @@ const init = async () => {
// Init globals // Init globals
HARDWARE.session.user = sessionUser(); HARDWARE.session.user = sessionUser();
HARDWARE.session.type = sessionType(); HARDWARE.session.type = sessionType();
HARDWARE.session.desktop = sessionDesktop();
HARDWARE.battery.level.path = getBatteryLevelPath(); HARDWARE.battery.level.path = getBatteryLevelPath();
HARDWARE.support.batteryLevel = HARDWARE.battery.level.path !== null;
HARDWARE.display.status.path = getDisplayStatusPath(); HARDWARE.display.status.path = getDisplayStatusPath();
HARDWARE.support.displayStatus = HARDWARE.display.status.path !== null; HARDWARE.display.status.command = getDisplayStatusCommand();
HARDWARE.display.brightness.path = getDisplayBrightnessPath(); HARDWARE.display.brightness.path = getDisplayBrightnessPath();
HARDWARE.support.displayBrightness = HARDWARE.display.brightness.path !== null;
HARDWARE.display.brightness.max = getDisplayBrightnessMax(); HARDWARE.display.brightness.max = getDisplayBrightnessMax();
HARDWARE.support.keyboardVisibility = processRuns("squeekboard"); HARDWARE.support = checkSupport();
HARDWARE.initialized = true; HARDWARE.initialized = true;
// Show supported features // Show supported features
@@ -65,6 +65,7 @@ const init = async () => {
// Show session infos // Show session infos
console.log("\nUser:", HARDWARE.session.user); console.log("\nUser:", HARDWARE.session.user);
console.log("Session:", HARDWARE.session.type); console.log("Session:", HARDWARE.session.type);
console.log("Desktop:", HARDWARE.session.desktop);
// Show device infos // Show device infos
console.log("\nModel:", getModel()); console.log("\nModel:", getModel());
@@ -80,9 +81,23 @@ const init = async () => {
console.log("Processor Temperature:", getProcessorTemperature()); console.log("Processor Temperature:", getProcessorTemperature());
// Show hardware infos // Show hardware infos
console.log(`\nBattery Level [${HARDWARE.battery.level.path}]:`, getBatteryLevel()); const unsupported = "unsupported";
console.log(`Display Status [${HARDWARE.display.status.path}]:`, getDisplayStatus()); console.log(
console.log(`Display Brightness [${HARDWARE.display.brightness.path}]:`, getDisplayBrightness(), "\n"); `\nBattery Level [${HARDWARE.battery.level.path || unsupported}]:`,
`${getBatteryLevel() || unsupported}`,
);
console.log(
`Display Status [${HARDWARE.display.status.path || unsupported}]:`,
`${getDisplayStatus() || unsupported} (${HARDWARE.display.status.command || unsupported})`,
);
console.log(
`Display Brightness [${HARDWARE.display.brightness.path || unsupported}]:`,
`${getDisplayBrightness() || unsupported}`,
);
console.log(
`Keyboard Visibility [${HARDWARE.support.keyboardVisibility ? "squeekboard" : unsupported}]:`,
`${getKeyboardVisibility() || unsupported}\n`,
);
// Check for keyboard visibility // Check for keyboard visibility
setKeyboardVisibility("OFF", (reply, error) => { setKeyboardVisibility("OFF", (reply, error) => {
@@ -134,15 +149,15 @@ const update = async () => {
}; };
/** /**
* Verifies device compatibility by checking the presence of necessary sys paths. * Verifies system compatibility by checking the presence of necessary sys paths.
* *
* @returns {bool} Returns true if all paths exists. * @returns {bool} Returns true if all paths exists.
*/ */
const compatibleDevice = () => { const compatibleSystem = () => {
if (os.platform() !== "linux") { if (os.platform() !== "linux") {
return false; return false;
} }
const paths = ["/sys/class/drm", "/sys/class/backlight", "/sys/class/thermal"]; const paths = ["/sys/class/drm", "/sys/class/backlight", "/sys/class/power_supply", "/sys/class/thermal"];
return paths.every((path) => fs.existsSync(path)); return paths.every((path) => fs.existsSync(path));
}; };
@@ -187,6 +202,35 @@ const sessionType = () => {
]); ]);
}; };
/**
* Gets the desktop environment name by checking environment variables.
*
* @returns {string} Returns desktop environment name or 'unknown' if not detected.
*/
const sessionDesktop = () => {
const envs = ["XDG_CURRENT_DESKTOP", "XDG_DESKTOP_SESSION", "DESKTOP_SESSION"];
const names = envs.map((env) => process.env[env]).filter(Boolean);
return (names.join(":") || "unknown").toLowerCase();
};
/**
* Checks supported features based on global properties.
*
* @returns {Object} Returns support object with boolean values.
*/
const checkSupport = () => {
const battery = HARDWARE.battery.level;
const status = HARDWARE.display.status;
const brightness = HARDWARE.display.brightness;
const keyboard = processRuns("squeekboard");
return {
batteryLevel: battery.path !== null,
displayStatus: status.path !== null && status.command !== null,
displayBrightness: brightness.path !== null && brightness.max !== null,
keyboardVisibility: keyboard === true,
};
};
/** /**
* Gets the model name using `/sys/firmware/devicetree/base/model` or `/sys/class/dmi/id/product_name`. * Gets the model name using `/sys/firmware/devicetree/base/model` or `/sys/class/dmi/id/product_name`.
* *
@@ -371,7 +415,31 @@ const getDisplayStatusPath = () => {
}; };
/** /**
* Gets the current display power status using `wlopm` or `xset`. * Gets the available display status command checking for `wlopm`, `kscreen-doctor` and `xset`.
*
* @returns {string|null} The display status command or null if nothing was found.
*/
const getDisplayStatusCommand = () => {
const type = HARDWARE.session.type;
const desktop = HARDWARE.session.desktop;
const mapping = {
wayland: [
{ command: "wlopm", desktops: ["labwc", "wayfire"] },
{ command: "kscreen-doctor", desktops: ["kde", "plasma"] },
],
x11: [{ command: "xset", desktops: ["*"] }],
}[type];
for (const map of mapping || []) {
if (commandExists(map.command) && map.desktops.some((d) => d === "*" || desktop.includes(d))) {
return map.command;
}
}
console.warn("No display command available");
return null;
};
/**
* Gets the current display power status using the available command.
* *
* @returns {string|null} The display status as 'ON'/'OFF' or null if an error occurs. * @returns {string|null} The display status as 'ON'/'OFF' or null if an error occurs.
*/ */
@@ -379,31 +447,34 @@ const getDisplayStatus = () => {
if (!HARDWARE.support.displayStatus) { if (!HARDWARE.support.displayStatus) {
return null; return null;
} }
switch (HARDWARE.session.type) { switch (HARDWARE.display.status.command) {
case "wayland": case "wlopm":
if (commandExists("wlopm")) { const wlopm = execSyncCommand("wlopm", []);
const status = execSyncCommand("wlopm", []); if (wlopm !== null) {
if (status !== null) { const out = wlopm.split("\n")[0].split(" ");
const out = status.split("\n")[0].split(" ");
return out.pop().toUpperCase(); return out.pop().toUpperCase();
} }
break;
case "kscreen-doctor":
const kdoc = execSyncCommand("kscreen-doctor", ["--dpms", "show"]);
if (kdoc !== null) {
const out = kdoc.split("\n")[0].split(" ");
return out.pop().toUpperCase();
} }
break; break;
case "x11": case "xset":
if (commandExists("xset")) { const xset = execSyncCommand("xset", ["-q"]);
const status = execSyncCommand("xset", ["-q"]); if (xset !== null) {
if (status !== null) { const on = /Monitor is On/.test(xset);
const on = /Monitor is On/.test(status);
return on ? "ON" : "OFF"; return on ? "ON" : "OFF";
} }
}
break; break;
} }
return null; return null;
}; };
/** /**
* Sets the display power status using `wlopm` or `xset`. * Sets the display power status using the available command.
* *
* This function takes a desired status ('ON' or 'OFF') and executes * This function takes a desired status ('ON' or 'OFF') and executes
* the appropriate command to set the display status. * the appropriate command to set the display status.
@@ -421,19 +492,16 @@ const setDisplayStatus = (status, callback = null) => {
if (typeof callback === "function") callback(null, "Invalid status"); if (typeof callback === "function") callback(null, "Invalid status");
return; return;
} }
switch (HARDWARE.session.type) { switch (HARDWARE.display.status.command) {
case "wayland": case "wlopm":
if (commandExists("wlopm")) {
execAsyncCommand("wlopm", [`--${status.toLowerCase()}`, "*"], callback); execAsyncCommand("wlopm", [`--${status.toLowerCase()}`, "*"], callback);
}
break; break;
case "x11": case "kscreen-doctor":
if (commandExists("xset")) { execAsyncCommand("kscreen-doctor", ["--dpms", status.toLowerCase()], callback);
break;
case "xset":
execAsyncCommand("xset", ["dpms", "force", status.toLowerCase()], callback); execAsyncCommand("xset", ["dpms", "force", status.toLowerCase()], callback);
}
break; break;
default:
if (typeof callback === "function") callback(null, "Invalid session type");
} }
}; };
@@ -460,7 +528,7 @@ const getDisplayBrightnessPath = () => {
* @returns {number|null} The brightness maximum value or null if an error occurs. * @returns {number|null} The brightness maximum value or null if an error occurs.
*/ */
const getDisplayBrightnessMax = () => { const getDisplayBrightnessMax = () => {
if (!HARDWARE.support.displayBrightness) { if (!HARDWARE.display.brightness.path) {
return null; return null;
} }
const max = fs.readFileSync(`${HARDWARE.display.brightness.path}/max_brightness`, "utf8").trim(); const max = fs.readFileSync(`${HARDWARE.display.brightness.path}/max_brightness`, "utf8").trim();
@@ -506,7 +574,7 @@ const setDisplayBrightness = (brightness, callback = null) => {
if (typeof callback === "function") callback(null, "Invalid brightness"); if (typeof callback === "function") callback(null, "Invalid brightness");
return; return;
} }
const max = HARDWARE.display.brightness.max; const max = HARDWARE.display.brightness.max || 1;
const value = Math.max(1, Math.min(Math.round((brightness / 100) * max), max)); const value = Math.max(1, Math.min(Math.round((brightness / 100) * max), max));
const proc = execAsyncCommand("sudo", ["tee", `${HARDWARE.display.brightness.path}/brightness`], callback); const proc = execAsyncCommand("sudo", ["tee", `${HARDWARE.display.brightness.path}/brightness`], callback);
proc.stdin.write(value.toString()); proc.stdin.write(value.toString());
@@ -686,9 +754,9 @@ const dbusCall = (path, method, values, callback = null) => {
const dest = `${iface} ${path} ${iface}.${method} ${values.join(" ")}`; const dest = `${iface} ${path} ${iface}.${method} ${values.join(" ")}`;
const args = ["--print-reply", "--type=method_call", `--dest=${dest}`]; const args = ["--print-reply", "--type=method_call", `--dest=${dest}`];
try { try {
const output = cpr.execSync([cmd, ...args].join(" "), { encoding: "utf8" }); const output = cpr.execSync([cmd, ...args].join(" ").trim(), { encoding: "utf8" });
if (typeof callback === "function") { if (typeof callback === "function") {
callback(output.trim().replace(/\0/g, ""), null); callback(output.trim().replace(/\0/g, "").split("\n").slice(1).join("\n").trim(), null);
} }
} catch (error) { } catch (error) {
console.error("Call D-Bus:", error.message); console.error("Call D-Bus:", error.message);
@@ -777,13 +845,9 @@ module.exports = {
getMemoryUsage, getMemoryUsage,
getProcessorUsage, getProcessorUsage,
getProcessorTemperature, getProcessorTemperature,
getBatteryLevelPath,
getBatteryLevel, getBatteryLevel,
getDisplayStatusPath,
getDisplayStatus, getDisplayStatus,
setDisplayStatus, setDisplayStatus,
getDisplayBrightnessPath,
getDisplayBrightnessMax,
getDisplayBrightness, getDisplayBrightness,
setDisplayBrightness, setDisplayBrightness,
getKeyboardVisibility, getKeyboardVisibility,
+12 -2
View File
@@ -337,14 +337,24 @@ const initDisplay = () => {
if (topic === config.command_topic) { if (topic === config.command_topic) {
const status = message.toString(); const status = message.toString();
console.log("Set Display Status:", status); console.log("Set Display Status:", status);
hardware.setDisplayStatus(status); hardware.setDisplayStatus(status, (reply, error) => {
if (error) {
console.warn("Failed:", error);
} else {
hardware.update(); hardware.update();
}
});
} else if (topic === config.brightness_command_topic) { } else if (topic === config.brightness_command_topic) {
const brightness = parseInt(message, 10); const brightness = parseInt(message, 10);
console.log("Set Display Brightness:", brightness); console.log("Set Display Brightness:", brightness);
hardware.setDisplayBrightness(brightness); hardware.setDisplayBrightness(brightness, (reply, error) => {
if (error) {
console.warn("Failed:", error);
} else {
hardware.update(); hardware.update();
} }
});
}
}) })
.subscribe(config.command_topic) .subscribe(config.command_topic)
.subscribe(config.brightness_command_topic); .subscribe(config.brightness_command_topic);