fixes waybar, adds quickshell
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
source = ~/.config/hypr/config/defaults.conf
|
source = ~/.config/hypr/config/defaults.conf
|
||||||
|
|
||||||
# Autostart wiki https://wiki.hyprland.org/0.45.0/Configuring/Keywords/#executing #
|
# Autostart wiki https://wiki.hyprland.org/0.45.0/Configuring/Keywords/#executing #
|
||||||
exec-once = /home/michaelb/.config/hypr/scripts/launch-waybar.sh &
|
exec-once = qs
|
||||||
exec-once = blueman-applet &
|
exec-once = blueman-applet &
|
||||||
exec-once = fcitx5 -d &
|
exec-once = fcitx5 -d &
|
||||||
exec-once = mako &
|
exec-once = mako &
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
monitor = eDP-2, highres@highrr, 0x0, 1, vrr, 1
|
monitor = eDP-2, highres@highrr, 0x0, 1, vrr, 1
|
||||||
|
|
||||||
#monitor = HDMI-A-1, highres@highrr, auto-right, 1, transform, 1
|
#monitor = HDMI-A-1, highres@highrr, auto-right, 1, transform, 1
|
||||||
monitor = desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1
|
#monitor = desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1
|
||||||
monitor = desc:Samsung Electric Company S24F350 H4ZK111233, highres@highrr, auto-right, 1, transform, 1
|
monitor = desc:Samsung Electric Company S24F350 H4ZK111233, highres@highrr, 1920x0, 1#, transform, 1
|
||||||
|
|
||||||
# monitor = HDMI-A-1, highres@highrr, auto-right, 1, vrr, 0
|
# monitor = HDMI-A-1, highres@highrr, auto-right, 1, vrr, 0
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Bar.qml - top panel
|
||||||
|
// Neighbouring types (Pill, ClockWidget, Theme, etc.) are auto-imported by QuickShell.
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "./modules"
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "quickshell-bar"
|
||||||
|
WlrLayershell.layer: WlrLayer.Top
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
margins {
|
||||||
|
left: 2
|
||||||
|
right: 2
|
||||||
|
bottom: 1
|
||||||
|
top: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: Theme.barHeight
|
||||||
|
exclusiveZone: Theme.barHeight
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
anchors.rightMargin: 4
|
||||||
|
spacing: Theme.spacing
|
||||||
|
|
||||||
|
// ─── LEFT ──────────────────────────────────────────
|
||||||
|
ClockWidget {}
|
||||||
|
WeatherWidget {}
|
||||||
|
SysTrayWidget {}
|
||||||
|
WorkspacesWidget { screen: root.screen }
|
||||||
|
MediaWidget {}
|
||||||
|
WindowTitleWidget { screen: root.screen }
|
||||||
|
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
|
||||||
|
// ─── RIGHT ─────────────────────────────────────────
|
||||||
|
CavaWidget {}
|
||||||
|
AudioWidget {}
|
||||||
|
MemoryWidget {}
|
||||||
|
CpuWidget {}
|
||||||
|
TemperatureWidget {}
|
||||||
|
BatteryWidget {}
|
||||||
|
BluetoothWidget {}
|
||||||
|
PowerProfilesWidget {}
|
||||||
|
PowerMenuWidget {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Exec.qml - fire-and-forget process launcher
|
||||||
|
// Usage from any file: Exec.run(["kitty", "-e", "btop"])
|
||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
function run(cmd) {
|
||||||
|
const proc = procPool.createObject(root, { command: cmd });
|
||||||
|
proc.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: procPool
|
||||||
|
Process {
|
||||||
|
running: false
|
||||||
|
onExited: destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Pill.qml - styled module container
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
default property alias content: inner.data
|
||||||
|
|
||||||
|
property bool hovered: mouseArea.containsMouse
|
||||||
|
signal clicked(var mouse)
|
||||||
|
signal scrolled(var wheel)
|
||||||
|
|
||||||
|
property real leftPadding: Theme.pillPadH
|
||||||
|
property real rightPadding: Theme.pillPadH
|
||||||
|
implicitWidth: inner.implicitWidth + leftPadding + rightPadding
|
||||||
|
implicitHeight: Theme.barHeight
|
||||||
|
radius: Theme.radius
|
||||||
|
color: hovered ? Theme.pillHover : Theme.pill
|
||||||
|
|
||||||
|
Behavior on color { ColorAnimation { duration: 150 } }
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: inner
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: root.leftPadding
|
||||||
|
spacing: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||||
|
onClicked: (m) => root.clicked(m)
|
||||||
|
onWheel: (w) => root.scrolled(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Theme.qml - global palette & dimensions
|
||||||
|
// QuickShell auto-discovers this; access from any file as `Theme.colorN` etc.
|
||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
// ── Wallust palette ──────────────────────────────────────
|
||||||
|
readonly property color background: "#252425"
|
||||||
|
readonly property color foreground: "#F9F1D9"
|
||||||
|
readonly property color color0: "#505051"
|
||||||
|
readonly property color color1: "#9C604E"
|
||||||
|
readonly property color color2: "#807A52"
|
||||||
|
readonly property color color3: "#908BAB"
|
||||||
|
readonly property color color4: "#B7815F"
|
||||||
|
readonly property color color5: "#B9BECA"
|
||||||
|
readonly property color color6: "#EED793"
|
||||||
|
readonly property color color7: "#EEE3C1"
|
||||||
|
readonly property color color8: "#A79F87"
|
||||||
|
|
||||||
|
// ── Derived / semantic ────────────────────────────────────
|
||||||
|
readonly property color pill: Qt.rgba(0.976, 0.945, 0.851, 0.15)
|
||||||
|
readonly property color pillHover: color2
|
||||||
|
readonly property color wsActive: color3
|
||||||
|
readonly property color wsUrgent: color1
|
||||||
|
|
||||||
|
// ── Typography ────────────────────────────────────────────
|
||||||
|
readonly property string fontSans: "Fira Sans Condensed"
|
||||||
|
readonly property string fontMono: "FiraCode Nerd Font"
|
||||||
|
readonly property int fontSize: 12
|
||||||
|
|
||||||
|
// ── Bar geometry ─────────────────────────────────────────
|
||||||
|
readonly property int barHeight: 28
|
||||||
|
readonly property int barPadding: 2
|
||||||
|
readonly property int radius: 5
|
||||||
|
readonly property int pillPadH: 10
|
||||||
|
readonly property int spacing: 4
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// modules/AudioWidget.qml - wireplumber volume, matches waybar style.
|
||||||
|
// Icon set mirrors waybar wireplumber format-icons (NerdFont).
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var node: Pipewire.defaultAudioSink
|
||||||
|
property bool muted: node?.audio.muted ?? false
|
||||||
|
property real vol: node?.audio.volume ?? 0
|
||||||
|
|
||||||
|
property string icon: {
|
||||||
|
if (muted || vol === 0) return " "
|
||||||
|
if (vol < 0.34) return " "
|
||||||
|
if (vol < 0.67) return " "
|
||||||
|
return " "
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.LeftButton) Exec.run(["pavucontrol"])
|
||||||
|
if (m.button === Qt.RightButton && node)
|
||||||
|
node.audio.muted = !node.audio.muted
|
||||||
|
}
|
||||||
|
|
||||||
|
onScrolled: (w) => {
|
||||||
|
if (!node) return
|
||||||
|
var delta = w.angleDelta.y > 0 ? 0.04 : -0.04
|
||||||
|
node.audio.volume = Math.max(0, Math.min(1.5, node.audio.volume + delta))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cava feeds into this on the left → right border only
|
||||||
|
leftPadding: 0
|
||||||
|
rightPadding: Theme.pillPadH
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.icon
|
||||||
|
font { family: Theme.fontMono; pixelSize: Theme.fontSize; }
|
||||||
|
color: "#fab387" // peach accent matching waybar foreground color for icon
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: root.muted ? "muted" : Math.round(root.vol * 100) + "%"
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// BatteryWidget.qml - battery icon + percentage
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
property int capacity: 100
|
||||||
|
property string status: "Unknown"
|
||||||
|
property bool charging: status === "Charging"
|
||||||
|
property bool plugged: status === "Full" || status === "Not charging"
|
||||||
|
property bool critical: capacity <= 15 && !charging
|
||||||
|
|
||||||
|
property string icon: {
|
||||||
|
if (charging) return " "
|
||||||
|
if (plugged) return " "
|
||||||
|
if (capacity > 80) return ""
|
||||||
|
if (capacity > 60) return ""
|
||||||
|
if (capacity > 40) return ""
|
||||||
|
if (capacity > 20) return ""
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.icon + " " + root.capacity + "%"
|
||||||
|
font.family: Theme.fontSans
|
||||||
|
font.pixelSize: Theme.fontSize
|
||||||
|
color: root.critical ? Theme.color1 : Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: batProc
|
||||||
|
running: false
|
||||||
|
command: ["bash", "-c",
|
||||||
|
"cat /sys/class/power_supply/BAT0/capacity 2>/dev/null; " +
|
||||||
|
"echo ---; " +
|
||||||
|
"cat /sys/class/power_supply/BAT0/status 2>/dev/null"]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
const parts = this.text.split("---")
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
const cap = parseInt(parts[0].trim())
|
||||||
|
if (!isNaN(cap)) root.capacity = cap
|
||||||
|
root.status = parts[1].trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: batProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 5000; running: true; repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: batProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// modules/BluetoothWidget.qml - ᛒ status / device alias
|
||||||
|
// Uses bluetoothctl via a polled Process (no BlueZ QML bindings in QS yet).
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
property string btStatus: "off" // "off" | "on" | "connected"
|
||||||
|
property string devAlias: ""
|
||||||
|
|
||||||
|
readonly property var bgColors: ({
|
||||||
|
"off": Qt.rgba(0.565, 0.545, 0.671, 0.3), // alpha(@color3, 0.3)
|
||||||
|
"on": Theme.color2,
|
||||||
|
"connected": Theme.color4,
|
||||||
|
})
|
||||||
|
// Override Pill's own color binding
|
||||||
|
color: bgColors[btStatus] ?? Theme.pill
|
||||||
|
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.LeftButton)
|
||||||
|
Exec.run(["blueman-manager"])
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: {
|
||||||
|
var s = root.btStatus
|
||||||
|
if (s === "connected") return "ᛒ " + (root.devAlias || "connected")
|
||||||
|
if (s === "on") return "ᛒ on"
|
||||||
|
return "ᛒ off"
|
||||||
|
}
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll bluetoothctl show + info every 5 s
|
||||||
|
Process {
|
||||||
|
id: btProc
|
||||||
|
running: false
|
||||||
|
command: ["bash", "-c",
|
||||||
|
"bluetoothctl show | grep -E 'Powered|Name'; " +
|
||||||
|
"bluetoothctl info 2>/dev/null | grep -E 'Name|Connected'"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: (line) => {
|
||||||
|
if (/Powered:\s+no/i.test(line)) { root.btStatus = "off"; root.devAlias = "" }
|
||||||
|
if (/Powered:\s+yes/i.test(line)) { if (root.btStatus === "off") root.btStatus = "on" }
|
||||||
|
if (/Connected:\s+yes/i.test(line)) root.btStatus = "connected"
|
||||||
|
if (/Connected:\s+no/i.test(line)) { if (root.btStatus === "connected") { root.btStatus = "on"; root.devAlias = "" } }
|
||||||
|
var match = /^\s+Name:\s+(.+)/.exec(line)
|
||||||
|
if (match && root.btStatus === "connected") root.devAlias = match[1].trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: btProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 5000; running: true; repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: btProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// CavaWidget.qml - audio visualiser via cava raw output
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var bars: Array(12).fill(0)
|
||||||
|
property bool silence: bars.every(v => v === 0)
|
||||||
|
|
||||||
|
readonly property var blocks: [" ","▁","▂","▃","▄","▅","▆","▇","█"]
|
||||||
|
|
||||||
|
implicitWidth: cavaRow.implicitWidth + Theme.pillPadH * 2
|
||||||
|
implicitHeight: Theme.barHeight
|
||||||
|
radius: Theme.radius
|
||||||
|
color: cavaHover.containsMouse ? Theme.pillHover : Theme.pill
|
||||||
|
Behavior on color { ColorAnimation { duration: 150 } }
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: cavaRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.bars.length
|
||||||
|
Text {
|
||||||
|
required property int index
|
||||||
|
text: root.silence
|
||||||
|
? " "
|
||||||
|
: root.blocks[Math.min(Math.floor(root.bars[index] / 28.5), 8)]
|
||||||
|
font.family: Theme.fontMono
|
||||||
|
font.pixelSize: Theme.fontSize + 1
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: cavaHover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: Exec.run(["pavucontrol"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the cava config once at startup, then run cava pointing at it.
|
||||||
|
Component.onCompleted: writeCfg.running = true
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: writeCfg
|
||||||
|
running: false
|
||||||
|
// Plain double-quoted string - no JS interpolation, bash sees ${VAR} verbatim.
|
||||||
|
command: ["bash", "-c",
|
||||||
|
"mkdir -p /tmp/qs-cava && cat > /tmp/qs-cava/cava.ini <<'CFG'\n" +
|
||||||
|
"[general]\n" +
|
||||||
|
"framerate = 30\n" +
|
||||||
|
"bars = 12\n" +
|
||||||
|
"[input]\n" +
|
||||||
|
"method = pipewire\n" +
|
||||||
|
"source = auto\n" +
|
||||||
|
"[smoothing]\n" +
|
||||||
|
"noise_reduction = 77\n" +
|
||||||
|
"monstercat = 1\n" +
|
||||||
|
"[output]\n" +
|
||||||
|
"method = raw\n" +
|
||||||
|
"raw_target = /dev/stdout\n" +
|
||||||
|
"data_format = ascii\n" +
|
||||||
|
"ascii_max_range = 255\n" +
|
||||||
|
"bar_delimiter = 59\n" +
|
||||||
|
"CFG\n"
|
||||||
|
]
|
||||||
|
onExited: cavaProc.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: cavaProc
|
||||||
|
running: false
|
||||||
|
command: ["cava", "-p", "/tmp/qs-cava/cava.ini"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: (line) => {
|
||||||
|
const parts = line.trim().replace(/;$/, "").split(";");
|
||||||
|
if (parts.length >= root.bars.length) {
|
||||||
|
root.bars = parts.slice(0, root.bars.length)
|
||||||
|
.map(v => parseInt(v) || 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: cavaProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// modules/ClockWidget.qml - " HH:MM DD Mon" (matches waybar clock format)
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.LeftButton)
|
||||||
|
Exec.run(["kitty", "-e", "calcure", "--class=float", "-T", "calcure"])
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: " " + Qt.formatDateTime(clock.now, "HH:mm") +
|
||||||
|
" " + Qt.formatDateTime(clock.now, "d MMM")
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update every 10 s (no need for per-second ticks)
|
||||||
|
QtObject {
|
||||||
|
id: clock
|
||||||
|
property var now: new Date()
|
||||||
|
property var timer: Timer {
|
||||||
|
interval: 10000; running: true; repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: clock.now = new Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// CpuWidget.qml - "X.XGHz | Y%"
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
property real freqGhz: 0
|
||||||
|
property int usagePct: 0
|
||||||
|
|
||||||
|
property int prevIdle: 0
|
||||||
|
property int prevTotal: 0
|
||||||
|
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.LeftButton) Exec.run(["kitty", "-e", "btop"])
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.freqGhz.toFixed(1) + "GHz | " + root.usagePct + "%"
|
||||||
|
font.family: Theme.fontSans
|
||||||
|
font.pixelSize: Theme.fontSize
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
// /proc/stat - first line is total CPU
|
||||||
|
Process {
|
||||||
|
id: statProc
|
||||||
|
running: false
|
||||||
|
command: ["bash", "-c", "head -1 /proc/stat && cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 2>/dev/null"]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
const lines = this.text.split("\n")
|
||||||
|
// line 0: cpu user nice system idle iowait irq softirq steal
|
||||||
|
const nums = lines[0].replace(/^cpu\s+/, "").split(/\s+/).map(Number)
|
||||||
|
const idle = (nums[3] || 0) + (nums[4] || 0)
|
||||||
|
const total = nums.reduce((s, v) => s + v, 0)
|
||||||
|
const dIdle = idle - root.prevIdle
|
||||||
|
const dTotal = total - root.prevTotal
|
||||||
|
if (dTotal > 0) root.usagePct = Math.round((1 - dIdle / dTotal) * 100)
|
||||||
|
root.prevIdle = idle
|
||||||
|
root.prevTotal = total
|
||||||
|
// line 1: current frequency in kHz
|
||||||
|
const khz = parseInt(lines[1] || "0")
|
||||||
|
if (!isNaN(khz) && khz > 0) root.freqGhz = khz / 1e6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: statProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1500; running: true; repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: statProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// modules/MediaWidget.qml
|
||||||
|
// Mirrors waybar custom/spotify - uses MPRIS via Quickshell.Services.Mpris.
|
||||||
|
// Shows: artist - title + spotify icon. Click to play/pause, scroll to skip.
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Pick the first active player (prefer spotify)
|
||||||
|
property MprisPlayer activePlayer: {
|
||||||
|
var players = Mpris.players.values
|
||||||
|
for (var i = 0; i < players.length; i++)
|
||||||
|
if (players[i].identity.toLowerCase() === "spotify") return players[i]
|
||||||
|
return players.length > 0 ? players[0] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
property string trackText: {
|
||||||
|
if (!activePlayer) return ""
|
||||||
|
var p = activePlayer
|
||||||
|
var info = ""
|
||||||
|
if (p.trackArtists && p.trackTitle)
|
||||||
|
info = p.trackArtists.join(", ") + " - " + p.trackTitle
|
||||||
|
else if (p.trackTitle)
|
||||||
|
info = p.trackTitle
|
||||||
|
if (info.length > 45) info = info.substring(0, 45) + "..."
|
||||||
|
if (p.playbackState !== MprisPlaybackState.Playing && info)
|
||||||
|
info = " " + info
|
||||||
|
return info + " " // trailing Nerd Font Spotify icon
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: trackText !== ""
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.trackText
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.foreground
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (!root.activePlayer) return
|
||||||
|
if (m.button === Qt.LeftButton)
|
||||||
|
root.activePlayer.togglePlaying()
|
||||||
|
}
|
||||||
|
|
||||||
|
onScrolled: (w) => {
|
||||||
|
if (!root.activePlayer) return
|
||||||
|
if (w.angleDelta.y > 0) root.activePlayer.next()
|
||||||
|
else root.activePlayer.previous()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// MemoryWidget.qml - " X.XX / Y GB"
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
property real usedGb: 0
|
||||||
|
property real totalGb: 0
|
||||||
|
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.LeftButton) Exec.run(["kitty", "-e", "btop"])
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: " " + root.usedGb.toFixed(2) + " / " + root.totalGb.toFixed(0) + " GB"
|
||||||
|
font.family: Theme.fontSans
|
||||||
|
font.pixelSize: Theme.fontSize
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: memProc
|
||||||
|
running: false
|
||||||
|
command: ["cat", "/proc/meminfo"]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
const text = this.text
|
||||||
|
const total = parseInt((/MemTotal:\s+(\d+)/.exec(text) || [])[1] || "0")
|
||||||
|
const avail = parseInt((/MemAvailable:\s+(\d+)/.exec(text) || [])[1] || "0")
|
||||||
|
root.totalGb = total / 1048576
|
||||||
|
root.usedGb = (total - avail) / 1048576
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: memProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 5000; running: true; repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: memProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
// modules/PowerMenuWidget.qml - ⏻ button with inline popup menu.
|
||||||
|
// Matches waybar custom/power with menu-actions.
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.LeftButton) menu.visible = !menu.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "⏻ "
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline drop-up popup - appears above the bar
|
||||||
|
Rectangle {
|
||||||
|
id: menu
|
||||||
|
visible: false
|
||||||
|
z: 100
|
||||||
|
|
||||||
|
width: 130
|
||||||
|
height: menuCol.implicitHeight + 16
|
||||||
|
radius: 5
|
||||||
|
color: Qt.rgba(0.086, 0.075, 0.125, 0.85)
|
||||||
|
border.color: Qt.rgba(1, 1, 1, 0.06)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
// Anchor above the pill
|
||||||
|
parent: root.parent // reparent to bar so z-ordering works
|
||||||
|
x: root.x + root.width - width
|
||||||
|
y: root.y - height - 4
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: menuCol
|
||||||
|
anchors { fill: parent; margins: 8 }
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
{ label: "Suspend", cmd: ["systemctl", "suspend"] },
|
||||||
|
{ label: "Hibernate", cmd: ["systemctl", "hibernate"] },
|
||||||
|
{ label: "Logout", cmd: ["hyprctl", "dispatch", "exit"] },
|
||||||
|
{ label: "Reboot", cmd: ["reboot"] },
|
||||||
|
{ label: "Shutdown", cmd: ["shutdown", "now"] },
|
||||||
|
]
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 26
|
||||||
|
radius: 5
|
||||||
|
color: itemHover.containsMouse
|
||||||
|
? Theme.pillHover
|
||||||
|
: "transparent"
|
||||||
|
Behavior on color { ColorAnimation { duration: 100 } }
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors { left: parent.left; verticalCenter: parent.verticalCenter; leftMargin: 8 }
|
||||||
|
text: modelData.label
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: itemHover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
menu.visible = false
|
||||||
|
Exec.run(modelData.cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// PowerProfilesWidget.qml - ⚡/⚖/🔋 + click-to-cycle
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
property string profile: "balanced"
|
||||||
|
|
||||||
|
readonly property var profileOrder: ["performance", "balanced", "power-saver"]
|
||||||
|
readonly property var icons: ({
|
||||||
|
"performance": "⚡",
|
||||||
|
"balanced": "⚖",
|
||||||
|
"power-saver": "🔋",
|
||||||
|
})
|
||||||
|
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button !== Qt.LeftButton) return;
|
||||||
|
const i = profileOrder.indexOf(root.profile);
|
||||||
|
const next = profileOrder[(i + 1) % profileOrder.length];
|
||||||
|
setProc.command = ["powerprofilesctl", "set", next];
|
||||||
|
setProc.running = true;
|
||||||
|
root.profile = next; // optimistic update
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: (root.icons[root.profile] ?? "⚡")
|
||||||
|
font.family: Theme.fontSans
|
||||||
|
font.pixelSize: Theme.fontSize
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current profile periodically
|
||||||
|
Process {
|
||||||
|
id: readProc
|
||||||
|
running: false
|
||||||
|
command: ["powerprofilesctl", "get"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: (line) => root.profile = line.trim()
|
||||||
|
}
|
||||||
|
onExited: readProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setter - command is rewritten on each click
|
||||||
|
Process {
|
||||||
|
id: setProc
|
||||||
|
running: false
|
||||||
|
command: ["true"]
|
||||||
|
onExited: setProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 2000
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: readProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// modules/SysTrayWidget.qml - SNI system tray (nm-applet, blueman ...)
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
color: "transparent"
|
||||||
|
radius: Theme.radius
|
||||||
|
|
||||||
|
implicitWidth: trayRow.implicitWidth + 6
|
||||||
|
implicitHeight: Theme.barHeight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: trayRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: SystemTray.items
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property SystemTrayItem modelData
|
||||||
|
width: 22; height: 22
|
||||||
|
radius: Theme.radius
|
||||||
|
color: trayHover.containsMouse
|
||||||
|
? Theme.pillHover
|
||||||
|
: Qt.rgba(0.976, 0.945, 0.851, 0.15)
|
||||||
|
|
||||||
|
Behavior on color { ColorAnimation { duration: 150 } }
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors { fill: parent; margins: 4 }
|
||||||
|
source: modelData.icon
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
smooth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: trayHover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.LeftButton)
|
||||||
|
modelData.activate()
|
||||||
|
else
|
||||||
|
modelData.contextMenu(mapToGlobal(mouseX, mouseY))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attention indicator dot
|
||||||
|
Rectangle {
|
||||||
|
visible: modelData.status === SystemTrayItem.NeedsAttention
|
||||||
|
width: 5; height: 5; radius: 2.5
|
||||||
|
color: Theme.wsUrgent
|
||||||
|
anchors { bottom: parent.bottom; right: parent.right; margins: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// TemperatureWidget.qml - CPU package temperature
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
property int tempC: 0
|
||||||
|
property bool critical: tempC >= 80
|
||||||
|
|
||||||
|
property string icon: tempC < 50 ? "" : tempC < 70 ? "" : ""
|
||||||
|
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.LeftButton) Exec.run(["xsensors"])
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.icon + " " + root.tempC + "°C"
|
||||||
|
font.family: Theme.fontSans
|
||||||
|
font.pixelSize: Theme.fontSize
|
||||||
|
color: root.critical ? Theme.color1 : Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read first available CPU package sensor - works regardless of hwmon number
|
||||||
|
Process {
|
||||||
|
id: tempProc
|
||||||
|
running: false
|
||||||
|
command: ["bash", "-c", "cat /sys/class/hwmon/hwmon*/temp1_input 2>/dev/null | head -1"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: (line) => {
|
||||||
|
const raw = parseInt(line.trim())
|
||||||
|
if (!isNaN(raw)) root.tempC = Math.round(raw / 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: tempProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 4000; running: true; repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: tempProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// modules/WeatherWidget.qml - wttr.in one-liner, refreshed hourly
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
property string weatherText: "..."
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: weatherText
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Fetch via curl ────────────────────────────────────────
|
||||||
|
Process {
|
||||||
|
id: curl
|
||||||
|
command: ["curl", "-s", "--max-time", "8", "https://wttr.in/?format=1"]
|
||||||
|
running: false
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: (line) => root.weatherText = line.trim()
|
||||||
|
}
|
||||||
|
onExited: curl.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 3600000 // 1 hour
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: curl.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// modules/WindowTitleWidget.qml - hyprland/window equivalent
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
property string title: {
|
||||||
|
var ws = Hyprland.focusedWorkspace
|
||||||
|
if (!ws) return ""
|
||||||
|
var win = ws.lastWindow
|
||||||
|
if (!win) return ""
|
||||||
|
var t = win.title ?? ""
|
||||||
|
return t.length > 60 ? t.substring(0, 60) + "..." : t
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: title !== ""
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.title
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.foreground
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// modules/WorkspacesWidget.qml
|
||||||
|
// Kanji workspace labels, per-monitor, matches waybar hyprland/workspaces.
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: wsRow.implicitWidth
|
||||||
|
implicitHeight: Theme.barHeight
|
||||||
|
|
||||||
|
// Filter workspaces that belong to this screen's monitor
|
||||||
|
property string monitorName: {
|
||||||
|
for (var i = 0; i < Hyprland.monitors.values.length; i++) {
|
||||||
|
var m = Hyprland.monitors.values[i]
|
||||||
|
if (m.name === screen.name) return m.name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: wsRow
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: {
|
||||||
|
// sort visible workspaces for this monitor
|
||||||
|
var all = Hyprland.workspaces.values
|
||||||
|
return all.filter(ws => ws.monitor && ws.monitor.name === root.monitorName)
|
||||||
|
.sort((a, b) => a.id - b.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
property bool isActive: modelData.id === (Hyprland.focusedWorkspace?.id ?? -1)
|
||||||
|
|
||||||
|
width: 32
|
||||||
|
height: Theme.barHeight - 4
|
||||||
|
radius: Theme.radius
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
color: isActive
|
||||||
|
? Theme.wsActive
|
||||||
|
: (wsBtn.containsMouse ? Theme.pillHover : Theme.pill)
|
||||||
|
|
||||||
|
Behavior on color { ColorAnimation { duration: 200 } }
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: Math.min(modelData.id - 1, 10 - 1)
|
||||||
|
font { family: Theme.fontSans; pixelSize: 11 }
|
||||||
|
color: Theme.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: wsBtn
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: Hyprland.dispatch("workspace " + modelData.id)
|
||||||
|
onWheel: (w) => Hyprland.dispatch(
|
||||||
|
"workspace " + (w.angleDelta.y > 0 ? "e+1" : "e-1"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// shell.qml - entry point
|
||||||
|
// QuickShell scans this directory and auto-imports neighbours (Bar, Theme, Exec, ...).
|
||||||
|
// Do NOT create a qmldir file - QuickShell synthesises one automatically.
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
ShellRoot {
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens
|
||||||
|
Bar {
|
||||||
|
required property var modelData
|
||||||
|
screen: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
@define-color cursor #AAA27D;
|
|
||||||
@define-color background #252425;
|
|
||||||
@define-color foreground #F9F1D9;
|
|
||||||
@define-color color0 #505051;
|
|
||||||
@define-color color1 #9C604E;
|
|
||||||
@define-color color2 #807A52;
|
|
||||||
@define-color color3 #908BAB;
|
|
||||||
@define-color color4 #B7815F;
|
|
||||||
@define-color color5 #B9BECA;
|
|
||||||
@define-color color6 #EED793;
|
|
||||||
@define-color color7 #EEE3C1;
|
|
||||||
@define-color color8 #A79F87;
|
|
||||||
@define-color color9 #9C604E;
|
|
||||||
@define-color color10 #807A52;
|
|
||||||
@define-color color11 #908BAB;
|
|
||||||
@define-color color12 #B7815F;
|
|
||||||
@define-color color13 #B9BECA;
|
|
||||||
@define-color color14 #EED793;
|
|
||||||
@define-color color15 #EEE3C1;
|
|
||||||
@@ -1,445 +0,0 @@
|
|||||||
{
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Global configuration
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
"layer": "top",
|
|
||||||
"swap-icon-label": true,
|
|
||||||
"position": "top",
|
|
||||||
|
|
||||||
"height": 36,
|
|
||||||
|
|
||||||
"margin-left": 2,
|
|
||||||
"margin-bottom": 1,
|
|
||||||
"margin-right": 2,
|
|
||||||
|
|
||||||
"spacing": 1, // Gaps between modules (4px)
|
|
||||||
|
|
||||||
"modules-left": [
|
|
||||||
"clock",
|
|
||||||
"custom/weather",
|
|
||||||
"tray",
|
|
||||||
//"custom/rofi",
|
|
||||||
"hyprland/workspaces",
|
|
||||||
//"hyprland/submap",
|
|
||||||
//"idle_inhibitor",
|
|
||||||
//"mpd",
|
|
||||||
"custom/spotify",
|
|
||||||
"hyprland/window",
|
|
||||||
],
|
|
||||||
// "modules-center": [
|
|
||||||
// //"custom/gammastep"
|
|
||||||
// ],
|
|
||||||
"modules-right": [
|
|
||||||
"cava",
|
|
||||||
"wireplumber",
|
|
||||||
//"wlr/taskbar",
|
|
||||||
//"custom/storage",
|
|
||||||
"memory",
|
|
||||||
"cpu",
|
|
||||||
"temperature",
|
|
||||||
"battery",
|
|
||||||
|
|
||||||
//"pulseaudio",
|
|
||||||
//"backlight",
|
|
||||||
"bluetooth",
|
|
||||||
//"custom/screenshot_t",
|
|
||||||
"custom/power_profiles",
|
|
||||||
"custom/power",
|
|
||||||
],
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Modules
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
"wlr/taskbar": {
|
|
||||||
"on-click": "activate",
|
|
||||||
"on-click-middle": "close",
|
|
||||||
"format": "{icon}",
|
|
||||||
"icon-size": 12,
|
|
||||||
"icon-theme": "Numix-Circle",
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/sp1": {
|
|
||||||
"format": " | ",
|
|
||||||
"tooltip": false,
|
|
||||||
},
|
|
||||||
"custom/sp2": {
|
|
||||||
"format": " |",
|
|
||||||
"tooltip": false,
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/rofi": {
|
|
||||||
"format": "",
|
|
||||||
"tooltip": false,
|
|
||||||
"on-click-right": "nwg-drawer",
|
|
||||||
"on-click": "wofi --show run",
|
|
||||||
"on-click-middle": "pkill -9 wofi",
|
|
||||||
},
|
|
||||||
"custom/screenshot_t": {
|
|
||||||
"format": " ",
|
|
||||||
"on-click": "~/.config/hypr/scripts/screenshot_full",
|
|
||||||
"on-click-right": "~/.config/hypr/scripts/screenshot_area",
|
|
||||||
},
|
|
||||||
|
|
||||||
"clock#1": {
|
|
||||||
"format": " {:%a}",
|
|
||||||
"tooltip": false,
|
|
||||||
"on-click": "kitty -e calcure --class=\"float\" -T calcure",
|
|
||||||
},
|
|
||||||
"clock#2": {
|
|
||||||
"format": " {:%d-%h-%Y}",
|
|
||||||
"tooltip": false,
|
|
||||||
"on-click": "kitty -e calcure --class=\"float\" -T calcure",
|
|
||||||
},
|
|
||||||
"clock#3": {
|
|
||||||
"format": " {:%H:%M:%S %p}",
|
|
||||||
"tooltip": false,
|
|
||||||
"on-click": "kitty -e calcure --class=\"float\" -T calcure",
|
|
||||||
},
|
|
||||||
|
|
||||||
"bluetooth": {
|
|
||||||
// "controller": "controller1", // specify the alias of the controller if there are more than 1 on the system
|
|
||||||
"format": "ᛒ {status}",
|
|
||||||
"format-connected": "ᛒ {device_alias}",
|
|
||||||
"format-disabled": "ᛒ off",
|
|
||||||
"tooltip-format": "{controller_alias}\t{controller_address}",
|
|
||||||
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{device_enumerate}",
|
|
||||||
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
|
|
||||||
"on-click": "blueman-manager",
|
|
||||||
},
|
|
||||||
|
|
||||||
"temperature": {
|
|
||||||
// "thermal-zone": 1,
|
|
||||||
"interval": 4,
|
|
||||||
//"hwmon-path": "/sys/class/hwmon/hwmon3/temp1_input",
|
|
||||||
"critical-threshold": 80,
|
|
||||||
// "format-critical": " {temperatureC}°C",
|
|
||||||
"format-critical": " {temperatureC}°C",
|
|
||||||
"format": "{icon} {temperatureC}°C",
|
|
||||||
"format-icons": ["", "", ""],
|
|
||||||
"max-length": 7,
|
|
||||||
"min-length": 7,
|
|
||||||
"on-click": "xsensors",
|
|
||||||
},
|
|
||||||
|
|
||||||
"memory": {
|
|
||||||
"interval": 30,
|
|
||||||
"format": " {used:0.2f} / {total:0.0f} GB",
|
|
||||||
"on-click": "kitty -e btop",
|
|
||||||
},
|
|
||||||
|
|
||||||
"battery": {
|
|
||||||
"interval": 2,
|
|
||||||
"states": {
|
|
||||||
"good": 95,
|
|
||||||
"warning": 30,
|
|
||||||
"critical": 15,
|
|
||||||
},
|
|
||||||
"format": "{icon} {capacity}%",
|
|
||||||
"format-charging": " {capacity}%",
|
|
||||||
"format-plugged": " {capacity}%",
|
|
||||||
"format-icons": ["", "", "", "", ""],
|
|
||||||
},
|
|
||||||
"network": {
|
|
||||||
"format-wifi": " {essid} ({signalStrength}%)",
|
|
||||||
"format-ethernet": "{ifname}: {ipaddr}/{cidr} ",
|
|
||||||
"format-linked": "{ifname} (No IP) ",
|
|
||||||
"format": "",
|
|
||||||
"format-disconnected": "",
|
|
||||||
"format-alt": "{ifname}: {ipaddr}/{cidr}",
|
|
||||||
"on-click": "wl-copy $(ip address show up scope global | grep inet | head -n1 | cut -d/ -f 1 | tr -d [:space:] | cut -c5-)",
|
|
||||||
"on-click-right": "wl-copy $(ip address show up scope global | grep inet6 | head -n1 | cut -d/ -f 1 | tr -d [:space:] | cut -c6-)",
|
|
||||||
"tooltip-format": " {bandwidthUpBits} {bandwidthDownBits}\n{ifname}\n{ipaddr}/{cidr}\n",
|
|
||||||
"tooltip-format-wifi": " {essid} {frequency}MHz\nStrength: {signaldBm}dBm ({signalStrength}%)\nIP: {ipaddr}/{cidr}\n {bandwidthUpBits} {bandwidthDownBits}",
|
|
||||||
"interval": 10,
|
|
||||||
},
|
|
||||||
"custom/storage": {
|
|
||||||
"format": " {}",
|
|
||||||
"format-alt": "{percentage}% ",
|
|
||||||
"format-alt-click": "click-right",
|
|
||||||
"return-type": "json",
|
|
||||||
"interval": 60,
|
|
||||||
"exec": "~/.config/waybar/modules/storage.sh",
|
|
||||||
},
|
|
||||||
|
|
||||||
"backlight": {
|
|
||||||
"device": "intel_backlight",
|
|
||||||
"format": "{icon} {percent}%",
|
|
||||||
"format-alt": "{percent}% {icon}",
|
|
||||||
"format-alt-click": "click-right",
|
|
||||||
//"format-icons": ["", ""],
|
|
||||||
"format-icons": ["", ""],
|
|
||||||
"on-scroll-down": "brightnessctl s 5%-",
|
|
||||||
"on-scroll-up": "brightnessctl s +5%",
|
|
||||||
},
|
|
||||||
"idle_inhibitor": {
|
|
||||||
"format": "{icon}",
|
|
||||||
"format-icons": {
|
|
||||||
"activated": "",
|
|
||||||
"deactivated": "",
|
|
||||||
},
|
|
||||||
"tooltip": "true",
|
|
||||||
},
|
|
||||||
"custom/weather": {
|
|
||||||
"format": "{}",
|
|
||||||
"format-alt": "{alt}: {}",
|
|
||||||
"format-alt-click": "click-right",
|
|
||||||
"interval": 3600,
|
|
||||||
"exec": "curl -s 'https://wttr.in/?format=1'",
|
|
||||||
//"return-type": "json",
|
|
||||||
//"exec": "~/.config/waybar/modules/weather.sh",
|
|
||||||
"exec-if": "ping wttr.in -c1",
|
|
||||||
},
|
|
||||||
"custom/pacman": {
|
|
||||||
"format": "<big></big> {}",
|
|
||||||
"interval": 3600, // every hour
|
|
||||||
"exec": "checkupdates | wc -l", // # of updates
|
|
||||||
"exec-if": "exit 0", // always run; consider advanced run conditions
|
|
||||||
"on-click": "alacritty -e 'paru'; pkill -SIGRTMIN+8 waybar", // update system
|
|
||||||
"signal": 8,
|
|
||||||
"max-length": 5,
|
|
||||||
"min-length": 3,
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/spotify": {
|
|
||||||
"exec": "~/.config/waybar/mediaplayer.py --player spotify",
|
|
||||||
"format": "{} ",
|
|
||||||
"return-type": "json",
|
|
||||||
"on-click": "playerctl play-pause",
|
|
||||||
"on-scroll-up": "playerctl next",
|
|
||||||
"on-scroll-down": "playerctl previous",
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/media": {
|
|
||||||
"format": "{0} {1}",
|
|
||||||
"return-type": "json",
|
|
||||||
"max-length": 40,
|
|
||||||
"format-icons": {
|
|
||||||
"spotify": "",
|
|
||||||
"default": "🎜",
|
|
||||||
},
|
|
||||||
"escape": true,
|
|
||||||
//"exec": "~/.config/waybar/mediaplayer.py" // Script in resources folder
|
|
||||||
// "exec": "~/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name
|
|
||||||
},
|
|
||||||
|
|
||||||
"clock": {
|
|
||||||
"format": " {:%H:%M %e %b}",
|
|
||||||
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
|
|
||||||
"today-format": "<b>{}</b>",
|
|
||||||
"on-click": "kitty -e calcure --class=\"float\" -T calcure",
|
|
||||||
},
|
|
||||||
|
|
||||||
"clock#date": {
|
|
||||||
"format": " {:%H:%M \n %e %b}",
|
|
||||||
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
|
|
||||||
"today-format": "<b>{}</b>",
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/gammastep": {
|
|
||||||
"interval": 5,
|
|
||||||
"return-type": "json",
|
|
||||||
"exec": {
|
|
||||||
"pre": "if unit_status=\"$(systemctl --user is-active gammastep)\"; then\nstatus=\"$unit_status ($(journalctl --user -u gammastep.service -g 'Period: ' | tail -1 | cut -d ':' -f6 | xargs))\"\nelse\nstatus=\"$unit_status\"\nfi",
|
|
||||||
"alt": "${status:-inactive}",
|
|
||||||
"tooltip": "Gammastep is $status",
|
|
||||||
},
|
|
||||||
"format": "{icon}",
|
|
||||||
"format-icons": {
|
|
||||||
"activating": " ",
|
|
||||||
"deactivating": " ",
|
|
||||||
"inactive": "? ",
|
|
||||||
"active (Night)": " ",
|
|
||||||
"active (Nighttime)": " ",
|
|
||||||
"active (Transition (Night)": " ",
|
|
||||||
"active (Transition (Nighttime)": " ",
|
|
||||||
"active (Day)": " ",
|
|
||||||
"active (Daytime)": " ",
|
|
||||||
"active (Transition (Day)": " ",
|
|
||||||
"active (Transition (Daytime)": " ",
|
|
||||||
},
|
|
||||||
"on-click": "systemctl --user is-active gammastep && systemctl --user stop gammastep || systemctl --user start gammastep",
|
|
||||||
},
|
|
||||||
|
|
||||||
"cpu": {
|
|
||||||
"interval": 1,
|
|
||||||
//"format": " {}%", // Icon: microchip
|
|
||||||
"format": "{max_frequency}GHz <span color=\"darkgray\">| {usage}%</span>",
|
|
||||||
"max-length": 13,
|
|
||||||
"min-length": 13,
|
|
||||||
},
|
|
||||||
|
|
||||||
"mpd": {
|
|
||||||
"max-length": 25,
|
|
||||||
"format": "<span foreground='#bb9af7'></span> {title}",
|
|
||||||
"format-paused": " {title}",
|
|
||||||
"format-stopped": "<span foreground='#bb9af7'></span>",
|
|
||||||
"format-disconnected": "",
|
|
||||||
"on-click": "mpc --quiet toggle",
|
|
||||||
"on-click-right": "mpc update; mpc ls | mpc add",
|
|
||||||
"on-click-middle": "alacritty -e ncmpcpp",
|
|
||||||
"on-scroll-up": "mpc --quiet prev",
|
|
||||||
"on-scroll-down": "mpc --quiet next",
|
|
||||||
"smooth-scrolling-threshold": 5,
|
|
||||||
"tooltip-format": "{title} - {artist} ({elapsedTime:%M:%S}/{totalTime:%H:%M:%S})",
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/title": {
|
|
||||||
"format": "{}",
|
|
||||||
"interval": 0,
|
|
||||||
"return-type": "json",
|
|
||||||
//"max-length": 35,
|
|
||||||
"tooltip": false,
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/title#name": {
|
|
||||||
"format": "{}",
|
|
||||||
"interval": 0,
|
|
||||||
"return-type": "json",
|
|
||||||
|
|
||||||
"max-length": 35,
|
|
||||||
"exec": "$HOME/.scripts/title",
|
|
||||||
},
|
|
||||||
|
|
||||||
/*"custom/keyboard": {
|
|
||||||
"format": " {}",
|
|
||||||
"interval": 1,
|
|
||||||
"exec": "$HOME/.config/waybar/get_kbdlayout.sh"
|
|
||||||
},*/
|
|
||||||
|
|
||||||
"hyprland/workspaces": {
|
|
||||||
"all-outputs": false,
|
|
||||||
"format": "{name}",
|
|
||||||
"format-icons": {
|
|
||||||
"1": "一",
|
|
||||||
"2": "二",
|
|
||||||
"3": "三",
|
|
||||||
"4": "四",
|
|
||||||
"5": "五",
|
|
||||||
"6": "六",
|
|
||||||
"7": "七",
|
|
||||||
"8": "八",
|
|
||||||
"9": "九",
|
|
||||||
"10": "十",
|
|
||||||
},
|
|
||||||
"on-scroll-up": "hyprctl dispatch workspace e+1 1>/dev/null",
|
|
||||||
"on-scroll-down": "hyprctl dispatch workspace e-1 1>/dev/null",
|
|
||||||
"sort-by-number": true,
|
|
||||||
"active-only": false,
|
|
||||||
},
|
|
||||||
|
|
||||||
"hyprland/window": {
|
|
||||||
"max-length": 200,
|
|
||||||
"separate-outputs": true,
|
|
||||||
},
|
|
||||||
|
|
||||||
"pulseaudio": {
|
|
||||||
"scroll-step": 3, // %, can be a float
|
|
||||||
"format": "{icon} {volume}% {format_source}",
|
|
||||||
"format-bluetooth": "{volume}% {icon} {format_source}",
|
|
||||||
"format-bluetooth-muted": " {icon} {format_source}",
|
|
||||||
"format-muted": " {format_source}",
|
|
||||||
//"format-source": "{volume}% ",
|
|
||||||
//"format-source-muted": "",
|
|
||||||
"format-source": "",
|
|
||||||
"format-source-muted": "",
|
|
||||||
"format-icons": {
|
|
||||||
"headphone": "",
|
|
||||||
"hands-free": "",
|
|
||||||
"headset": "",
|
|
||||||
"phone": "",
|
|
||||||
"portable": "",
|
|
||||||
"car": "",
|
|
||||||
"default": ["", "", ""],
|
|
||||||
},
|
|
||||||
"on-click": "pavucontrol",
|
|
||||||
"on-click-right": "amixer sset Master toggle",
|
|
||||||
},
|
|
||||||
|
|
||||||
"wireplumber": {
|
|
||||||
"on-click": "pavucontrol",
|
|
||||||
"on-click-right": "amixer sset Master toggle 1>/dev/null",
|
|
||||||
//on-click: "${wpctl} set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
|
||||||
//on-scroll-down: "${wpctl} set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 0.04+";
|
|
||||||
//on-scroll-up: "${wpctl} set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 0.04-";
|
|
||||||
"format": "<span foreground='#fab387'>{icon}</span> {volume}%",
|
|
||||||
"format-muted": " ",
|
|
||||||
"format-source": "",
|
|
||||||
"format-source-muted": "",
|
|
||||||
//"format-muted": "<span foreground='#fab387'> </span>",
|
|
||||||
//"format-icons": [ "<span foreground='#fab387'></span>" ]
|
|
||||||
"format-icons": {
|
|
||||||
"headphone": " ",
|
|
||||||
"hands-free": " ",
|
|
||||||
"headset": " ",
|
|
||||||
"phone": " ",
|
|
||||||
"portable": " ",
|
|
||||||
"car": " ",
|
|
||||||
"default": [" ", " ", " "],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"tray": {
|
|
||||||
"icon-size": 11,
|
|
||||||
"spacing": 5,
|
|
||||||
"show-passive-items": true,
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/power_profiles": {
|
|
||||||
"format": "{}",
|
|
||||||
"return-type": "json",
|
|
||||||
"interval": 2,
|
|
||||||
"exec": "~/.config/waybar/modules/power_profiles.py",
|
|
||||||
"menu": "on-click",
|
|
||||||
"menu-file": "~/.config/waybar/modules/power_profiles_menu.xml",
|
|
||||||
"menu-actions": {
|
|
||||||
"performance": "~/.config/waybar/modules/power_profiles.py --set performance",
|
|
||||||
"balanced": "~/.config/waybar/modules/power_profiles.py --set balanced",
|
|
||||||
"power-saver": "~/.config/waybar/modules/power_profiles.py --set power-saver",
|
|
||||||
},
|
|
||||||
"tooltip": true,
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/power": {
|
|
||||||
"format": "⏻ ",
|
|
||||||
"tooltip": false,
|
|
||||||
"menu": "on-click",
|
|
||||||
"menu-file": "~/.config/waybar/modules/power_menu.xml",
|
|
||||||
"menu-actions": {
|
|
||||||
"suspend": "systemctl suspend",
|
|
||||||
"hibernate": "systemctl hibernate",
|
|
||||||
"logout": "hyprctl dispatch exit",
|
|
||||||
"shutdown": "shutdown",
|
|
||||||
"reboot": "reboot",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"cava": {
|
|
||||||
"on-click": "pavucontrol",
|
|
||||||
// "cava_config": "$XDG_CONFIG_HOME/cava/cava.conf",
|
|
||||||
"framerate": 30,
|
|
||||||
"sensitivity": 3,
|
|
||||||
"autosens": 1,
|
|
||||||
"bars": 12,
|
|
||||||
"lower_cutoff_freq": 10,
|
|
||||||
"higher_cutoff_freq": 10000,
|
|
||||||
"hide_on_silence": false,
|
|
||||||
"format_silent": "quiet",
|
|
||||||
"method": "pipewire",
|
|
||||||
"source": "auto",
|
|
||||||
"stereo": false,
|
|
||||||
"reverse": false,
|
|
||||||
"bar_delimiter": 0,
|
|
||||||
"bar_spacing": 1,
|
|
||||||
"monstercat": true,
|
|
||||||
"waves": true,
|
|
||||||
"noise_reduction": 0.77,
|
|
||||||
"input_delay": 2,
|
|
||||||
"eq": { "1": 1.8, "2": 1.4, "3": 1.1, "4": 1.0, "5": 1.0 },
|
|
||||||
"format-icons": ["", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"],
|
|
||||||
"actions": {
|
|
||||||
"on-click-right": "mode",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import argparse
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
import signal
|
|
||||||
import gi
|
|
||||||
import json
|
|
||||||
gi.require_version('Playerctl', '2.0')
|
|
||||||
from gi.repository import Playerctl, GLib
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def write_output(text, player):
|
|
||||||
logger.info('Writing output')
|
|
||||||
|
|
||||||
output = {'text': text,
|
|
||||||
'class': 'custom-' + player.props.player_name,
|
|
||||||
'alt': player.props.player_name}
|
|
||||||
|
|
||||||
sys.stdout.write(json.dumps(output) + '\n')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def on_play(player, status, manager):
|
|
||||||
logger.info('Received new playback status')
|
|
||||||
on_metadata(player, player.props.metadata, manager)
|
|
||||||
|
|
||||||
|
|
||||||
def on_metadata(player, metadata, manager):
|
|
||||||
logger.info('Received new metadata')
|
|
||||||
track_info = ''
|
|
||||||
|
|
||||||
if player.props.player_name == 'spotify' and \
|
|
||||||
'mpris:trackid' in metadata.keys() and \
|
|
||||||
':ad:' in player.props.metadata['mpris:trackid']:
|
|
||||||
track_info = 'AD PLAYING'
|
|
||||||
elif player.get_artist() != '' and player.get_title() != '':
|
|
||||||
track_info = '{artist} - {title}'.format(artist=player.get_artist(),
|
|
||||||
title=player.get_title())
|
|
||||||
else:
|
|
||||||
track_info = player.get_title()
|
|
||||||
|
|
||||||
if player.props.status != 'Playing' and track_info:
|
|
||||||
track_info = ' ' + track_info
|
|
||||||
write_output(track_info, player)
|
|
||||||
|
|
||||||
|
|
||||||
def on_player_appeared(manager, player, selected_player=None):
|
|
||||||
if player is not None and (selected_player is None or player.name == selected_player):
|
|
||||||
init_player(manager, player)
|
|
||||||
else:
|
|
||||||
logger.debug("New player appeared, but it's not the selected player, skipping")
|
|
||||||
|
|
||||||
|
|
||||||
def on_player_vanished(manager, player):
|
|
||||||
logger.info('Player has vanished')
|
|
||||||
sys.stdout.write('\n')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def init_player(manager, name):
|
|
||||||
logger.debug('Initialize player: {player}'.format(player=name.name))
|
|
||||||
player = Playerctl.Player.new_from_name(name)
|
|
||||||
player.connect('playback-status', on_play, manager)
|
|
||||||
player.connect('metadata', on_metadata, manager)
|
|
||||||
manager.manage_player(player)
|
|
||||||
on_metadata(player, player.props.metadata, manager)
|
|
||||||
|
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
|
||||||
logger.debug('Received signal to stop, exiting')
|
|
||||||
sys.stdout.write('\n')
|
|
||||||
sys.stdout.flush()
|
|
||||||
# loop.quit()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_arguments():
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
|
|
||||||
# Increase verbosity with every occurance of -v
|
|
||||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
|
||||||
|
|
||||||
# Define for which player we're listening
|
|
||||||
parser.add_argument('--player')
|
|
||||||
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
arguments = parse_arguments()
|
|
||||||
|
|
||||||
# Initialize logging
|
|
||||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG,
|
|
||||||
format='%(name)s %(levelname)s %(message)s')
|
|
||||||
|
|
||||||
# Logging is set by default to WARN and higher.
|
|
||||||
# With every occurrence of -v it's lowered by one
|
|
||||||
logger.setLevel(max((3 - arguments.verbose) * 10, 0))
|
|
||||||
|
|
||||||
# Log the sent command line arguments
|
|
||||||
logger.debug('Arguments received {}'.format(vars(arguments)))
|
|
||||||
|
|
||||||
manager = Playerctl.PlayerManager()
|
|
||||||
loop = GLib.MainLoop()
|
|
||||||
|
|
||||||
manager.connect('name-appeared', lambda *args: on_player_appeared(*args, arguments.player))
|
|
||||||
manager.connect('player-vanished', on_player_vanished)
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
|
||||||
|
|
||||||
for player in manager.props.player_names:
|
|
||||||
if arguments.player is not None and arguments.player != player.name:
|
|
||||||
logger.debug('{player} is not the filtered player, skipping it'
|
|
||||||
.format(player=player.name)
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
init_player(manager, player)
|
|
||||||
|
|
||||||
loop.run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
import os
|
|
||||||
import imaplib
|
|
||||||
|
|
||||||
import mailsecrets
|
|
||||||
|
|
||||||
def getmails(username, password, server):
|
|
||||||
imap = imaplib.IMAP4_SSL(server, 993)
|
|
||||||
imap.login(username, password)
|
|
||||||
imap.select('INBOX')
|
|
||||||
ustatus, uresponse = imap.uid('search', None, 'UNSEEN')
|
|
||||||
if ustatus == 'OK':
|
|
||||||
unread_msg_nums = uresponse[0].split()
|
|
||||||
else:
|
|
||||||
unread_msg_nums = []
|
|
||||||
|
|
||||||
fstatus, fresponse = imap.uid('search', None, 'FLAGGED')
|
|
||||||
if fstatus == 'OK':
|
|
||||||
flagged_msg_nums = fresponse[0].split()
|
|
||||||
else:
|
|
||||||
flagged_msg_nums = []
|
|
||||||
|
|
||||||
return [len(unread_msg_nums), len(flagged_msg_nums)]
|
|
||||||
|
|
||||||
ping = os.system("ping " + mailsecrets.server + " -c1 > /dev/null 2>&1")
|
|
||||||
if ping == 0:
|
|
||||||
mails = getmails(mailsecrets.username, mailsecrets.password, mailsecrets.server)
|
|
||||||
text = ''
|
|
||||||
alt = ''
|
|
||||||
|
|
||||||
if mails[0] > 0:
|
|
||||||
text = alt = str(mails[0])
|
|
||||||
if mails[1] > 0:
|
|
||||||
alt = str(mails[1]) + " " + alt
|
|
||||||
else:
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
print('{"text":"' + text + '", "alt": "' + alt + '"}')
|
|
||||||
|
|
||||||
else:
|
|
||||||
exit(1)
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<interface>
|
|
||||||
<object class="GtkMenu" id="menu">
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="suspend">
|
|
||||||
<property name="label">Suspend</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="hibernat">
|
|
||||||
<property name="label">Hibernate</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="logout">
|
|
||||||
<property name="label">Logout</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="shutdown">
|
|
||||||
<property name="label">Shutdown</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSeparatorMenuItem" id="delimiter1"/>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="reboot">
|
|
||||||
<property name="label">Reboot</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import subprocess
|
|
||||||
import json
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def get_current_profile():
|
|
||||||
"""Get the current power profile"""
|
|
||||||
try:
|
|
||||||
result = subprocess.run(
|
|
||||||
["powerprofilesctl", "get"],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
timeout=5
|
|
||||||
)
|
|
||||||
if result.returncode == 0:
|
|
||||||
return result.stdout.strip()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error getting profile: {e}", file=sys.stderr)
|
|
||||||
return "unknown"
|
|
||||||
|
|
||||||
def get_available_profiles():
|
|
||||||
"""Get list of available power profiles"""
|
|
||||||
try:
|
|
||||||
result = subprocess.run(
|
|
||||||
["powerprofilesctl", "list"],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
timeout=5
|
|
||||||
)
|
|
||||||
if result.returncode == 0:
|
|
||||||
profiles = []
|
|
||||||
for line in result.stdout.split('\n'):
|
|
||||||
line = line.strip()
|
|
||||||
if line and ':' in line and not line.startswith('Cpu') and not line.startswith('Platform') and not line.startswith('Degraded'):
|
|
||||||
profile_name = line.split(':')[0].strip()
|
|
||||||
if profile_name and not profile_name.startswith('*'):
|
|
||||||
profiles.append(profile_name)
|
|
||||||
elif profile_name.startswith('*'):
|
|
||||||
profiles.append(profile_name[1:].strip())
|
|
||||||
return profiles
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error getting profiles: {e}", file=sys.stderr)
|
|
||||||
return []
|
|
||||||
|
|
||||||
def set_profile(profile):
|
|
||||||
"""Set the power profile"""
|
|
||||||
try:
|
|
||||||
subprocess.run(
|
|
||||||
["powerprofilesctl", "set", profile],
|
|
||||||
capture_output=True,
|
|
||||||
timeout=5
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error setting profile: {e}", file=sys.stderr)
|
|
||||||
|
|
||||||
def write_menu_file(menu_path, profiles, current):
|
|
||||||
"""Write a GTK menu XML file for Waybar."""
|
|
||||||
items = []
|
|
||||||
if not profiles:
|
|
||||||
items.append(
|
|
||||||
" <child>\n"
|
|
||||||
" <object class=\"GtkMenuItem\" id=\"none\">\n"
|
|
||||||
" <property name=\"label\">No profiles</property>\n"
|
|
||||||
" <property name=\"sensitive\">false</property>\n"
|
|
||||||
" </object>\n"
|
|
||||||
" </child>\n"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for profile in profiles:
|
|
||||||
label = profile
|
|
||||||
if profile == current:
|
|
||||||
label = f"{profile} (current)"
|
|
||||||
items.append(
|
|
||||||
" <child>\n"
|
|
||||||
f" <object class=\"GtkMenuItem\" id=\"{profile}\">\n"
|
|
||||||
f" <property name=\"label\">{label}</property>\n"
|
|
||||||
" </object>\n"
|
|
||||||
" </child>\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
content = (
|
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
|
||||||
"<interface>\n"
|
|
||||||
" <object class=\"GtkMenu\" id=\"menu\">\n"
|
|
||||||
+ "".join(items) +
|
|
||||||
" </object>\n"
|
|
||||||
"</interface>\n"
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
menu_path.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
menu_path.write_text(content)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error writing menu file: {e}", file=sys.stderr)
|
|
||||||
|
|
||||||
def format_output(profile):
|
|
||||||
"""Format output for waybar"""
|
|
||||||
# Icons for each profile
|
|
||||||
icons = {
|
|
||||||
"performance": "⚡",
|
|
||||||
"balanced": "⚖",
|
|
||||||
"power-saver": "🔋"
|
|
||||||
}
|
|
||||||
|
|
||||||
icon = icons.get(profile, "⚡")
|
|
||||||
|
|
||||||
output = {
|
|
||||||
"text": f"{icon}",
|
|
||||||
"alt": profile,
|
|
||||||
"class": f"power-profile-{profile}",
|
|
||||||
"tooltip": f"Current: {profile}"
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.dumps(output)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
menu_path = Path("~/.config/waybar/modules/power_profiles_menu.xml").expanduser()
|
|
||||||
|
|
||||||
if len(sys.argv) > 2 and sys.argv[1] == "--set":
|
|
||||||
target = sys.argv[2]
|
|
||||||
set_profile(target)
|
|
||||||
current = get_current_profile()
|
|
||||||
available = get_available_profiles()
|
|
||||||
write_menu_file(menu_path, available, current)
|
|
||||||
print(format_output(current))
|
|
||||||
return
|
|
||||||
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == "--next":
|
|
||||||
# Cycle to next profile
|
|
||||||
current = get_current_profile()
|
|
||||||
available = get_available_profiles()
|
|
||||||
write_menu_file(menu_path, available, current)
|
|
||||||
|
|
||||||
if current in available and available:
|
|
||||||
idx = available.index(current)
|
|
||||||
next_profile = available[(idx + 1) % len(available)]
|
|
||||||
set_profile(next_profile)
|
|
||||||
print(format_output(next_profile))
|
|
||||||
else:
|
|
||||||
print(format_output(current))
|
|
||||||
return
|
|
||||||
|
|
||||||
# Just display current profile
|
|
||||||
current = get_current_profile()
|
|
||||||
available = get_available_profiles()
|
|
||||||
write_menu_file(menu_path, available, current)
|
|
||||||
print(format_output(current))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<interface>
|
|
||||||
<object class="GtkMenu" id="menu">
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="performance">
|
|
||||||
<property name="label">performance</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="balanced">
|
|
||||||
<property name="label">balanced</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="power-saver">
|
|
||||||
<property name="label">power-saver (current)</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
class=$(playerctl metadata --player=spotify --format '{{lc(status)}}')
|
|
||||||
icon=""
|
|
||||||
|
|
||||||
if [[ $class == "playing" ]]; then
|
|
||||||
info=$(playerctl metadata --player=spotify --format '{{artist}} - {{title}}')
|
|
||||||
if [[ ${#info} > 40 ]]; then
|
|
||||||
info=$(echo $info | cut -c1-40)"..."
|
|
||||||
fi
|
|
||||||
text=$info" "$icon
|
|
||||||
elif [[ $class == "paused" ]]; then
|
|
||||||
text=$icon
|
|
||||||
elif [[ $class == "stopped" ]]; then
|
|
||||||
text=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "{\"text\":\""$text"\", \"class\":\""$class"\"}"
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
mount="/"
|
|
||||||
warning=20
|
|
||||||
critical=10
|
|
||||||
|
|
||||||
df -h -P -l "$mount" | awk -v warning=$warning -v critical=$critical '
|
|
||||||
/\/.*/ {
|
|
||||||
text=$4
|
|
||||||
tooltip="Filesystem: "$1"\rSize: "$2"\rUsed: "$3"\rAvail: "$4"\rUse%: "$5"\rMounted on: "$6
|
|
||||||
use=$5
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
END {
|
|
||||||
class=""
|
|
||||||
gsub(/%$/,"",use)
|
|
||||||
if ((100 - use) < critical) {
|
|
||||||
class="critical"
|
|
||||||
} else if ((100 - use) < warning) {
|
|
||||||
class="warning"
|
|
||||||
}
|
|
||||||
print "{\"text\":\""text"\", \"percentage\":"use",\"tooltip\":\""tooltip"\", \"class\":\""class"\"}"
|
|
||||||
}
|
|
||||||
'
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
cachedir=~/.cache/rbn
|
|
||||||
cachefile=${0##*/}-$1
|
|
||||||
|
|
||||||
if [ ! -d $cachedir ]; then
|
|
||||||
mkdir -p $cachedir
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f $cachedir/$cachefile ]; then
|
|
||||||
touch $cachedir/$cachefile
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Save current IFS
|
|
||||||
SAVEIFS=$IFS
|
|
||||||
# Change IFS to new line.
|
|
||||||
IFS=$'\n'
|
|
||||||
|
|
||||||
cacheage=$(($(date +%s) - $(stat -c '%Y' "$cachedir/$cachefile")))
|
|
||||||
if [ $cacheage -gt 1740 ] || [ ! -s $cachedir/$cachefile ]; then
|
|
||||||
data=($(curl -s https://en.wttr.in/$1\?0qnT 2>&1))
|
|
||||||
echo ${data[0]} | cut -f1 -d, > $cachedir/$cachefile
|
|
||||||
echo ${data[1]} | sed -E 's/^.{15}//' >> $cachedir/$cachefile
|
|
||||||
echo ${data[2]} | sed -E 's/^.{15}//' >> $cachedir/$cachefile
|
|
||||||
fi
|
|
||||||
|
|
||||||
weather=($(cat $cachedir/$cachefile))
|
|
||||||
|
|
||||||
# Restore IFSClear
|
|
||||||
IFS=$SAVEIFS
|
|
||||||
|
|
||||||
temperature=$(echo ${weather[2]} | sed -E 's/([[:digit:]])+\.\./\1 to /g')
|
|
||||||
|
|
||||||
#echo ${weather[1]##*,}
|
|
||||||
|
|
||||||
# https://fontawesome.com/icons?s=solid&c=weather
|
|
||||||
case $(echo ${weather[1]##*,} | tr '[:upper:]' '[:lower:]') in
|
|
||||||
"clear" | "sunny")
|
|
||||||
condition=""
|
|
||||||
;;
|
|
||||||
"partly cloudy")
|
|
||||||
condition="杖"
|
|
||||||
;;
|
|
||||||
"cloudy")
|
|
||||||
condition=""
|
|
||||||
;;
|
|
||||||
"overcast")
|
|
||||||
condition=""
|
|
||||||
;;
|
|
||||||
"mist" | "fog" | "freezing fog")
|
|
||||||
condition=""
|
|
||||||
;;
|
|
||||||
"patchy rain possible" | "patchy light drizzle" | "light drizzle" | "patchy light rain" | "light rain" | "light rain shower" | "rain")
|
|
||||||
condition=""
|
|
||||||
;;
|
|
||||||
"moderate rain at times" | "moderate rain" | "heavy rain at times" | "heavy rain" | "moderate or heavy rain shower" | "torrential rain shower" | "rain shower")
|
|
||||||
condition=""
|
|
||||||
;;
|
|
||||||
"patchy snow possible" | "patchy sleet possible" | "patchy freezing drizzle possible" | "freezing drizzle" | "heavy freezing drizzle" | "light freezing rain" | "moderate or heavy freezing rain" | "light sleet" | "ice pellets" | "light sleet showers" | "moderate or heavy sleet showers")
|
|
||||||
condition="ﭽ"
|
|
||||||
;;
|
|
||||||
"blowing snow" | "moderate or heavy sleet" | "patchy light snow" | "light snow" | "light snow showers")
|
|
||||||
condition="流"
|
|
||||||
;;
|
|
||||||
"blizzard" | "patchy moderate snow" | "moderate snow" | "patchy heavy snow" | "heavy snow" | "moderate or heavy snow with thunder" | "moderate or heavy snow showers")
|
|
||||||
condition="ﰕ"
|
|
||||||
;;
|
|
||||||
"thundery outbreaks possible" | "patchy light rain with thunder" | "moderate or heavy rain with thunder" | "patchy light snow with thunder")
|
|
||||||
condition=""
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
condition=""
|
|
||||||
echo -e "{\"text\":\""$condition"\", \"alt\":\""${weather[0]}"\", \"tooltip\":\""${weather[0]}: $temperature ${weather[1]}"\"}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
#echo $temp $condition
|
|
||||||
|
|
||||||
echo -e "{\"text\":\""$temperature $condition"\", \"alt\":\""${weather[0]}"\", \"tooltip\":\""${weather[0]}: $temperature ${weather[1]}"\"}"
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
/* Import wallust colors */
|
|
||||||
@import url("colors-wallust.css");
|
|
||||||
|
|
||||||
/* Global defaults */
|
|
||||||
* {
|
|
||||||
font-family:
|
|
||||||
"Fira Sans Condensed", "Font Awesome 6 Free", FontAwesome, Roboto,
|
|
||||||
Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
|
||||||
min-height: 0;
|
|
||||||
transition: background-color 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modules-left,
|
|
||||||
.modules-right {
|
|
||||||
background-color: rgba(17, 24, 39, 0.1);
|
|
||||||
margin: 2px;
|
|
||||||
padding: 1px 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The whole bar */
|
|
||||||
#waybar {
|
|
||||||
background-color: alpha(@background, 0);
|
|
||||||
color: @foreground;
|
|
||||||
border-radius: 0px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
window#waybar.hidden {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
window#waybar.empty #window {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Workspaces --- */
|
|
||||||
#workspaces button {
|
|
||||||
padding: 3px 10px;
|
|
||||||
margin: 2px;
|
|
||||||
border-radius: 3px;
|
|
||||||
color: @foreground;
|
|
||||||
background-color: alpha(@foreground, 0.15);
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
font-size: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.active {
|
|
||||||
color: @foreground;
|
|
||||||
background: @color3;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button:hover {
|
|
||||||
background: @color2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.urgent {
|
|
||||||
background-color: @color9;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces {
|
|
||||||
background-color: transparent;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Tray --- */
|
|
||||||
#tray {
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray > * {
|
|
||||||
background-color: alpha(@foreground, 0.15);
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray > * > image,
|
|
||||||
#tray > * image {
|
|
||||||
padding: 0px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray > *:hover {
|
|
||||||
background: @color2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray > .needs-attention {
|
|
||||||
background-color: @color9;
|
|
||||||
}
|
|
||||||
|
|
||||||
#window {
|
|
||||||
background-color: alpha(@background, 0.1);
|
|
||||||
color: @foreground;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 1px 10px;
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Shared module styling --- */
|
|
||||||
#clock,
|
|
||||||
#battery,
|
|
||||||
#cpu,
|
|
||||||
#memory,
|
|
||||||
#custom-storage,
|
|
||||||
#custom-weather,
|
|
||||||
#bluetooth,
|
|
||||||
#custom-media,
|
|
||||||
#custom-power_profiles,
|
|
||||||
#custom-power,
|
|
||||||
#temperature,
|
|
||||||
#backlight,
|
|
||||||
#network,
|
|
||||||
#pulseaudio,
|
|
||||||
#wireplumber,
|
|
||||||
#cava {
|
|
||||||
background-color: alpha(@foreground, 0.15);
|
|
||||||
color: @foreground;
|
|
||||||
padding: 0px 10px;
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#clock:hover,
|
|
||||||
#battery:hover,
|
|
||||||
#cpu:hover,
|
|
||||||
#memory:hover,
|
|
||||||
#custom-storage:hover,
|
|
||||||
#custom-weather:hover,
|
|
||||||
#bluetooth:hover,
|
|
||||||
#custom-media:hover,
|
|
||||||
#custom-power_profiles:hover,
|
|
||||||
#custom-power:hover,
|
|
||||||
#temperature:hover,
|
|
||||||
#backlight:hover,
|
|
||||||
#network:hover,
|
|
||||||
#wireplumber:hover,
|
|
||||||
#pulseaudio:hover,
|
|
||||||
#cava:hover {
|
|
||||||
background: @color2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#wireplumber {
|
|
||||||
border-radius: 0px 3px 3px 0px;
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cava {
|
|
||||||
border-radius: 3px 0px 0px 3px;
|
|
||||||
margin-right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bluetooth {
|
|
||||||
background-color: alpha(@color3, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#bluetooth.disabled {
|
|
||||||
}
|
|
||||||
|
|
||||||
#bluetooth.on {
|
|
||||||
background-color: alpha(@color2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#bluetooth.connected {
|
|
||||||
background-color: alpha(@color4, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#bluetooth.connected:hover,
|
|
||||||
#bluetooth.on:hover {
|
|
||||||
background-color: alpha(@color3, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#battery.critical:not(.charging) {
|
|
||||||
color: @color1;
|
|
||||||
background-color: alpha(@color2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#temperature.critical {
|
|
||||||
background-color: alpha(@color2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#idle_inhibitor {
|
|
||||||
background-color: rgba(45, 52, 54, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#idle_inhibitor.activated {
|
|
||||||
background-color: rgba(236, 240, 241, 0.5); /* bleibt gleich */
|
|
||||||
color: rgba(45, 52, 54, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Language + keyboard state */
|
|
||||||
#language,
|
|
||||||
#keyboard-state {
|
|
||||||
padding: 0 0px;
|
|
||||||
margin: 0 5px;
|
|
||||||
min-width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#language {
|
|
||||||
background: rgba(0, 176, 147, 0.5);
|
|
||||||
color: rgba(116, 8, 100, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state {
|
|
||||||
background: rgba(151, 225, 173, 0.5);
|
|
||||||
color: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state > label {
|
|
||||||
padding: 0 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state > label.locked {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#taskbar {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#taskbar button {
|
|
||||||
padding: 0px 3px 0px 6px;
|
|
||||||
margin: 2px 1px;
|
|
||||||
border-radius: 6px;
|
|
||||||
color: @foreground;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#taskbar button.active {
|
|
||||||
background: alpha(@foreground, 0.2);
|
|
||||||
transition: all 0.4s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu {
|
|
||||||
border-radius: 5px;
|
|
||||||
border: 0px;
|
|
||||||
background: rgba(22, 19, 32, 0.5);
|
|
||||||
color: #b5e8e0;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
menuitem {
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
# Terminate already running bar instances
|
|
||||||
killall -q waybar
|
|
||||||
|
|
||||||
# Wait until the processes have been shut down
|
|
||||||
while pgrep -x waybar >/dev/null; do sleep 1; done
|
|
||||||
|
|
||||||
# Launch main
|
|
||||||
waybar &
|
|
||||||
Reference in New Issue
Block a user