diff --git a/assets/icon-missing.png b/assets/icon-missing.png
deleted file mode 100644
index 434f071..0000000
Binary files a/assets/icon-missing.png and /dev/null differ
diff --git a/assets/icon-missing.svg b/assets/icon-missing.svg
deleted file mode 100644
index 71f1e2e..0000000
--- a/assets/icon-missing.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
diff --git a/config/Config.qml b/config/Config.qml
index 16481c8..d8b9e22 100644
--- a/config/Config.qml
+++ b/config/Config.qml
@@ -161,8 +161,6 @@ Singleton {
}
readonly property QtObject services: QtObject {
- readonly property real batteryWarning: 0.15
-
readonly property string weatherLocation: ""
readonly property bool useFahrenheit: [Locale.ImperialUSSystem, Locale.ImperialSystem].includes(Qt.locale().measurementSystem)
@@ -171,8 +169,6 @@ Singleton {
readonly property string sunsetFrom: "21:00"
readonly property string sunsetTo: "9:00"
readonly property int sunsetTemperature: 4500
-
- readonly property real brightnessExp: 4.0
}
readonly property QtObject session: QtObject {
diff --git a/modules/bar/modules/StatusIcons.qml b/modules/bar/modules/StatusIcons.qml
index f5bff04..59a9c17 100644
--- a/modules/bar/modules/StatusIcons.qml
+++ b/modules/bar/modules/StatusIcons.qml
@@ -95,7 +95,7 @@ Container {
readonly property bool hasBattery: UPower.displayDevice.isLaptopBattery
readonly property real percentage: UPower.displayDevice.percentage
readonly property bool charging: !UPower.onBattery && batteryText.text !== "100"
- readonly property bool warning: UPower.onBattery && percentage < Config.services.batteryWarning + 0.01
+ readonly property bool warning: UPower.onBattery && percentage < 0.15
text: {
if (!hasBattery) {
@@ -142,7 +142,7 @@ Container {
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 0.5
- text: Math.floor(battery.percentage * 100)
+ text: Math.round(battery.percentage * 100)
color: battery.warning ? Config.colors.batteryWarning : Config.colors.bg
font.family: Config.font.family.mono
font.pointSize: 6
diff --git a/modules/bar/popouts/ActiveWindow.qml b/modules/bar/popouts/ActiveWindow.qml
index 673e52f..bb1e625 100644
--- a/modules/bar/popouts/ActiveWindow.qml
+++ b/modules/bar/popouts/ActiveWindow.qml
@@ -73,13 +73,6 @@ Item {
property: "scale"
to: 1
}
- // Fixes really weird transition bug
- NumberAnimation {
- targets: header
- property: "height"
- to: Config.font.size.normal
- duration: 20
- }
}
// Reveal on window title change
@@ -118,7 +111,6 @@ Item {
anchors.right: infobox.right
anchors.top: infobox.top
anchors.margins: 12
- anchors.topMargin: 11
}
}
diff --git a/modules/bar/popouts/Battery.qml b/modules/bar/popouts/Battery.qml
index ff1fafa..5553f32 100644
--- a/modules/bar/popouts/Battery.qml
+++ b/modules/bar/popouts/Battery.qml
@@ -13,15 +13,14 @@ ColumnLayout {
spacing: 4
- readonly property bool hasBattery: UPower.displayDevice.isLaptopBattery
- readonly property real percentage: UPower.displayDevice.percentage
- readonly property bool warning: UPower.onBattery && percentage < Config.services.batteryWarning + 0.01
- readonly property color color: warning ? Config.colors.batteryWarning : Config.colors.battery
+ readonly property color color: UPower.onBattery && UPower.displayDevice.percentage < 0.15 ?
+ Config.colors.batteryWarning :
+ Config.colors.battery
Loader {
Layout.alignment: Qt.AlignHCenter
- active: root.hasBattery
+ active: UPower.displayDevice.isLaptopBattery
asynchronous: true
height: active ? (item?.implicitHeight ?? 0) : 0
@@ -77,7 +76,7 @@ ColumnLayout {
radiusX: (meter.size + meter.thickness) / 2 + meter.padding
radiusY: radiusX
startAngle: -90 - meter.angle / 2
- sweepAngle: meter.angle * root.percentage
+ sweepAngle: meter.angle * UPower.displayDevice.percentage
}
Behavior on strokeColor {
@@ -99,7 +98,7 @@ ColumnLayout {
CustomText {
anchors.horizontalCenter: parent.horizontalCenter
- text: Math.floor(root.percentage * 100) + "%"
+ text: Math.round(UPower.displayDevice.percentage * 100) + "%"
font.pointSize: Config.font.size.largest
}
diff --git a/modules/bar/popouts/Content.qml b/modules/bar/popouts/Content.qml
index 18e50ae..1ab7152 100644
--- a/modules/bar/popouts/Content.qml
+++ b/modules/bar/popouts/Content.qml
@@ -67,7 +67,7 @@ Item {
name: "battery"
source: "Battery.qml"
color: UPower.displayDevice.isLaptopBattery &&
- UPower.onBattery && UPower.displayDevice.percentage < Config.services.batteryWarning + 0.01 ?
+ UPower.onBattery && UPower.displayDevice.percentage < 0.15 ?
Config.colors.batteryWarning :
Config.colors.battery
}
diff --git a/modules/bar/popouts/Wrapper.qml b/modules/bar/popouts/Wrapper.qml
index df5184f..305f17f 100644
--- a/modules/bar/popouts/Wrapper.qml
+++ b/modules/bar/popouts/Wrapper.qml
@@ -13,6 +13,7 @@ Item {
id: root
required property PersistentProperties uiState
+ required property ShellScreen screen
readonly property real nonAnimWidth: content.implicitWidth
readonly property real nonAnimHeight: y > 0 || hasCurrent ? content.implicitHeight : 0
diff --git a/modules/dashboard/Mixer.qml b/modules/dashboard/Mixer.qml
index a83ae58..7ca119e 100644
--- a/modules/dashboard/Mixer.qml
+++ b/modules/dashboard/Mixer.qml
@@ -110,8 +110,8 @@ Item {
source: {
const icon = entry.modelData.properties["application.icon-name"];
if (icon)
- return Icons.getAppIcon(icon, "icon-missing");
- Icons.getAppIcon(entry.modelData.name, "icon-missing")
+ return Icons.getAppIcon(icon, "image-missing");
+ Icons.getAppIcon(entry.modelData.name, "image-missing")
}
}
diff --git a/modules/dashboard/Workspaces.qml b/modules/dashboard/Workspaces.qml
index 6f5db42..4feeef9 100644
--- a/modules/dashboard/Workspaces.qml
+++ b/modules/dashboard/Workspaces.qml
@@ -290,7 +290,6 @@ Item {
property var ipc: modelData.lastIpcObject
opacity: ipc && ipc.at && !ipc.hidden ? 1 : 0
- visible: opacity > 0
property real nonAnimX: ipc?.at ? (ipc.at[0] - preview.monX) * preview.sizeRatio : 0
property real nonAnimY: ipc?.at ? (ipc.at[1] - preview.monY) * preview.sizeRatio : 0
diff --git a/modules/dashboard/dash/Weather.qml b/modules/dashboard/dash/Weather.qml
index ee61b91..af520fc 100644
--- a/modules/dashboard/dash/Weather.qml
+++ b/modules/dashboard/dash/Weather.qml
@@ -83,6 +83,7 @@ Item {
anchors.left: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: 12
+ anchors.bottomMargin: 1
animate: true
text: Weather.feelsLike
diff --git a/modules/launcher/items/AppItem.qml b/modules/launcher/items/AppItem.qml
index 3d8b4e6..dea01c2 100644
--- a/modules/launcher/items/AppItem.qml
+++ b/modules/launcher/items/AppItem.qml
@@ -37,7 +37,7 @@ Item {
IconImage {
id: icon
- source: Quickshell.iconPath(root.modelData?.icon, "icon-missing")
+ source: Quickshell.iconPath(root.modelData?.icon, "image-missing")
implicitSize: parent.height * 0.9
anchors.verticalCenter: parent.verticalCenter
diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml
index 1449135..3495b96 100644
--- a/modules/osd/Content.qml
+++ b/modules/osd/Content.qml
@@ -11,6 +11,7 @@ Item {
id: root
required property var uiState
+ required property Brightness.Monitor monitor
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
@@ -94,10 +95,13 @@ Item {
implicitHeight: Config.osd.sliderLength
function onWheel(event: WheelEvent) {
+ const monitor = root.monitor;
+ if (!monitor)
+ return;
if (event.angleDelta.y > 0)
- Brightness.setBrightness(Brightness.brightness + 0.1);
+ monitor.setBrightness(monitor.brightness + 0.1);
else if (event.angleDelta.y < 0)
- Brightness.setBrightness(Brightness.brightness - 0.1);
+ monitor.setBrightness(monitor.brightness - 0.1);
}
CustomFilledSlider {
@@ -105,8 +109,8 @@ Item {
color: Config.colors.brightness
icon: Icons.getBrightnessIcon(value)
- value: Brightness.brightness
- onMoved: Brightness.setBrightness(value)
+ value: root.monitor?.brightness ?? 0
+ onMoved: root.monitor?.setBrightness(value)
}
}
}
diff --git a/modules/osd/Interactions.qml b/modules/osd/Interactions.qml
index 4c2f05f..3036a35 100644
--- a/modules/osd/Interactions.qml
+++ b/modules/osd/Interactions.qml
@@ -7,8 +7,10 @@ Scope {
id: root
required property PersistentProperties uiState
+ required property ShellScreen screen
required property bool hovered
required property bool suppressed
+ readonly property Brightness.Monitor monitor: Brightness.getMonitorForScreen(screen)
function show(): void {
if (!root.suppressed) {
@@ -32,7 +34,7 @@ Scope {
}
Connections {
- target: Brightness
+ target: root.monitor
function onBrightnessChanged(): void {
if (root.uiState.osdBrightnessReact)
diff --git a/modules/osd/Wrapper.qml b/modules/osd/Wrapper.qml
index a764f19..109909c 100644
--- a/modules/osd/Wrapper.qml
+++ b/modules/osd/Wrapper.qml
@@ -8,6 +8,7 @@ Item {
id: root
required property var uiState
+ required property ShellScreen screen
visible: width > 0
implicitWidth: 0
@@ -60,5 +61,6 @@ Item {
id: content
uiState: root.uiState
+ monitor: Brightness.getMonitorForScreen(root.screen)
}
}
diff --git a/modules/ui/Interactions.qml b/modules/ui/Interactions.qml
index 3e08345..f3d601e 100644
--- a/modules/ui/Interactions.qml
+++ b/modules/ui/Interactions.qml
@@ -62,10 +62,6 @@ CustomMouseArea {
if (y < Config.bar.height && !popoutsSuppressed && !popouts.persistent) {
bar.checkPopout(x);
}
- // Hide bar popouts (if user moves mouse along edge)
- if (y > popouts.nonAnimHeight + Config.bar.height) {
- popouts.hasCurrent = false;
- }
// Show osd on hover
const showOsd = inRightPanel(panels.osd, x, y);
@@ -120,6 +116,7 @@ CustomMouseArea {
Osd.Interactions {
uiState: root.uiState
+ screen: root.screen
hovered: root.osdHovered
suppressed: root.osdSuppressed
}
diff --git a/modules/ui/Panels.qml b/modules/ui/Panels.qml
index b1b39c1..f808fa2 100644
--- a/modules/ui/Panels.qml
+++ b/modules/ui/Panels.qml
@@ -13,6 +13,7 @@ Item {
id: root
required property PersistentProperties uiState
+ required property ShellScreen screen
required property Item bar
readonly property alias popouts: popouts
@@ -30,12 +31,14 @@ Item {
id: popouts
uiState: root.uiState
+ screen: root.screen
}
Osd.Wrapper {
id: osd
uiState: root.uiState
+ screen: root.screen
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
diff --git a/modules/ui/UI.qml b/modules/ui/UI.qml
index e988ab0..896cdc4 100644
--- a/modules/ui/UI.qml
+++ b/modules/ui/UI.qml
@@ -110,6 +110,7 @@ Variants {
visible: !uiState.uiState.hidden
uiState: uiState.uiState
+ screen: scope.modelData
bar: bar
}
}
diff --git a/package.nix b/package.nix
index 79feb46..e1e3c4d 100644
--- a/package.nix
+++ b/package.nix
@@ -55,9 +55,6 @@ in stdenv.mkDerivation {
cp -R . $out/share/${pname}
- mkdir -p $out/share/icons/hicolor/512x512/apps/
- cp assets/icon-missing.png $out/share/icons/hicolor/512x512/apps/
-
makeWrapper ${quickshell}/bin/qs $out/bin/${pname} \
--prefix PATH : "${lib.makeBinPath runtimeDeps}" \
--set FONTCONFIG_FILE "${fontconfig}" \
diff --git a/services/Brightness.qml b/services/Brightness.qml
index b7f8bc9..856b0c7 100644
--- a/services/Brightness.qml
+++ b/services/Brightness.qml
@@ -12,38 +12,58 @@ Singleton {
reloadableId: "brightness"
- property real brightness: 1.0
- property real maxBrightness: 0.0
+ property list ddcMonitors: []
+ readonly property list monitors: variants.instances
+ property bool appleDisplayPresent: false
+
+ function getMonitorForScreen(screen: ShellScreen): var {
+ return monitors.find(m => m.modelData === screen);
+ }
function increaseBrightness(): void {
- setBrightness(brightness + Config.osd.brightnessIncrement);
+ const focusedName = Hypr.focusedMonitor.name;
+ const monitor = monitors.find(m => focusedName === m.modelData.name);
+ if (monitor)
+ monitor.setBrightness(monitor.brightness + Config.osd.brightnessIncrement);
}
function decreaseBrightness(): void {
- setBrightness(brightness - Config.osd.brightnessIncrement);
+ const focusedName = Hypr.focusedMonitor.name;
+ const monitor = monitors.find(m => focusedName === m.modelData.name);
+ if (monitor)
+ monitor.setBrightness(monitor.brightness - Config.osd.brightnessIncrement);
}
- function setBrightness(value: real): void {
- value = Math.max(0, Math.min(1, value));
- if (Math.abs(brightness - value) < 0.01) return;
- brightness = value;
-
- const exp = Config.services.brightnessExp;
- const raw = Math.round((value ** exp) * maxBrightness);
- Quickshell.execDetached(["brightnessctl", "s", `${raw}`]);
+ onMonitorsChanged: {
+ ddcMonitors = [];
+ ddcProc.running = true;
+ }
+
+ Variants {
+ id: variants
+
+ model: Quickshell.screens
+
+ Monitor {}
}
- Component.onCompleted: initProc.running = true
Process {
- id: initProc
- command: ["sh", "-c", "echo $(brightnessctl g) $(brightnessctl m)"]
+ running: true
+ command: ["sh", "-c", "asdbctl get"] // To avoid warnings if asdbctl is not installed
stdout: StdioCollector {
- onStreamFinished: {
- const exp = Config.services.brightnessExp;
- const [cur, max] = text.split(" ");
- root.maxBrightness = parseInt(max);
- root.brightness = (parseInt(cur) / root.maxBrightness) ** (1 / exp);
- }
+ onStreamFinished: root.appleDisplayPresent = text.trim().length > 0
+ }
+ }
+
+ Process {
+ id: ddcProc
+
+ command: ["ddcutil", "detect", "--brief"]
+ stdout: StdioCollector {
+ onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({
+ model: d.match(/Monitor:.*:(.*):.*/)[1],
+ busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1]
+ }))
}
}
@@ -90,4 +110,77 @@ Singleton {
root.decreaseBrightness();
}
}
+
+ component Monitor: QtObject {
+ id: monitor
+
+ required property ShellScreen modelData
+ readonly property bool isDdc: root.ddcMonitors.some(m => m.model === modelData.model)
+ readonly property string busNum: root.ddcMonitors.find(m => m.model === modelData.model)?.busNum ?? ""
+ readonly property bool isAppleDisplay: root.appleDisplayPresent && modelData.model.startsWith("StudioDisplay")
+ property real brightness
+ property real queuedBrightness: NaN
+
+ readonly property Process initProc: Process {
+ stdout: StdioCollector {
+ onStreamFinished: {
+ if (monitor.isAppleDisplay) {
+ const val = parseInt(text.trim());
+ monitor.brightness = val / 101;
+ } else {
+ const [, , , cur, max] = text.split(" ");
+ monitor.brightness = parseInt(cur) / parseInt(max);
+ }
+ }
+ }
+ }
+
+ readonly property Timer timer: Timer {
+ interval: 500
+ onTriggered: {
+ if (!isNaN(monitor.queuedBrightness)) {
+ monitor.setBrightness(monitor.queuedBrightness);
+ monitor.queuedBrightness = NaN;
+ }
+ }
+ }
+
+ function setBrightness(value: real): void {
+ value = Math.max(0, Math.min(1, value));
+ const rounded = Math.round(value * 100);
+ if (Math.round(brightness * 100) === rounded)
+ return;
+
+ if (isDdc && timer.running) {
+ queuedBrightness = value;
+ return;
+ }
+
+ brightness = value;
+
+ if (isAppleDisplay)
+ Quickshell.execDetached(["asdbctl", "set", rounded]);
+ else if (isDdc)
+ Quickshell.execDetached(["ddcutil", "-b", busNum, "setvcp", "10", rounded]);
+ else
+ Quickshell.execDetached(["brightnessctl", "s", `${rounded}%`]);
+
+ if (isDdc)
+ timer.restart();
+ }
+
+ function initBrightness(): void {
+ if (isAppleDisplay)
+ initProc.command = ["asdbctl", "get"];
+ else if (isDdc)
+ initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
+ else
+ initProc.command = ["sh", "-c", "echo a b c $(brightnessctl g) $(brightnessctl m)"];
+
+ initProc.running = true;
+ }
+
+ onBusNumChanged: initBrightness()
+ Component.onCompleted: initBrightness()
+ }
}