From 4bea16976bf366ced5f1b0d84eed295f94440630 Mon Sep 17 00:00:00 2001 From: Kajus Naujokaitis Date: Fri, 13 Dec 2024 13:36:00 +0200 Subject: [PATCH] WIP: Volume mixer widget --- modules/desktop/graphics/ewwbar.nix | 192 ++++++++++++------ .../microvm/virtualization/microvm/guivm.nix | 6 +- 2 files changed, 134 insertions(+), 64 deletions(-) diff --git a/modules/desktop/graphics/ewwbar.nix b/modules/desktop/graphics/ewwbar.nix index c66e4373d..4b669a19c 100644 --- a/modules/desktop/graphics/ewwbar.nix +++ b/modules/desktop/graphics/ewwbar.nix @@ -17,6 +17,10 @@ let }; inherit (config.ghaf.services.audio) pulseaudioTcpControlPort; + iconThemeBase = "/run/current-system/sw/share/icons/${ + if cfg.gtk.colorScheme == "prefer-dark" then "${cfg.gtk.iconTheme}-Dark" else cfg.gtk.iconTheme + }"; + launcher-icon = "${pkgs.ghaf-artwork}/icons/launcher.svg"; battery-0-icon = "${pkgs.ghaf-artwork}/icons/battery-0.svg"; @@ -50,8 +54,7 @@ let lock-icon = "${pkgs.ghaf-artwork}/icons/lock.svg"; logout-icon = "${pkgs.ghaf-artwork}/icons/logout.svg"; - - arrow-right-icon = "${pkgs.ghaf-artwork}/icons/arrow-right.svg"; + arrow-right-icon = "${iconThemeBase}/24x24/actions/adjustlevels.svg"; # Called by eww.yuck for updates and reloads ewwCmd = "${pkgs.eww}/bin/eww -c /etc/eww"; @@ -282,28 +285,37 @@ let pkgs.gawk pkgs.pulseaudio pkgs.pamixer + pkgs.gnused + pkgs.jq ]; bashOptions = [ ]; text = '' export PULSE_SERVER=audio-vm:${toString pulseaudioTcpControlPort} - icon() { - if [[ "$2" == "true" || "$1" -eq 0 ]]; then - echo "${volume-0-icon}" - elif [ "$1" -lt 25 ]; then - echo "${volume-1-icon}" - elif [ "$1" -lt 75 ]; then - echo "${volume-2-icon}" - else - echo "${volume-3-icon}" - fi - } - get() { - volume=$(pamixer --get-volume) - muted=$(pamixer --get-mute) - icon=$(icon "$volume" "$muted") - echo "{ \"level\": \"$volume\", \"muted\": \"$muted\", \"icon\": \"$icon\" }" + volume=$(pamixer --get-volume) + muted=$(pamixer --get-mute) + + sink_inputs_json=$(pactl -f json list sink-inputs | jq -c ' + map({ + level: (.volume."front-left".value_percent // "0" | sub("%$"; "")), + muted: (.mute // "false"), + name: (.properties."application.name" // ""), + icon_name: (.properties."application.icon_name" // ""), + id: (.index // "-1") + }) + ' || echo "[]") + + # Output the final JSON + jq -c --unbuffered -n --argjson sinkInputs "$sink_inputs_json" --arg level "$volume" --arg muted "$muted" ' + { + system: { + level: $level, + muted: $muted + }, + sinkInputs: $sinkInputs + } + ' } listen() { @@ -439,8 +451,8 @@ in ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defpoll keyboard_layout :interval "5s" "${pkgs.xorg.setxkbmap}/bin/setxkbmap -query | ${pkgs.gawk}/bin/awk '/layout/{print $2}' | tr a-z A-Z") (defpoll battery :interval "5s" :initial "{}" "${eww-bat}/bin/eww-bat get") - (deflisten brightness "${eww-brightness}/bin/eww-brightness listen") - (deflisten volume "${eww-volume}/bin/eww-volume listen") + (deflisten brightness :initial "{}" "${eww-brightness}/bin/eww-brightness listen") + (deflisten volume :initial "{}" "${eww-volume}/bin/eww-volume listen") (deflisten workspace :initial "1" "${ghaf-workspace}/bin/ghaf-workspace subscribe") (defvar calendar_day "date '+%d'") @@ -451,7 +463,8 @@ in (defvar brightness-popup-visible "false") (defvar workspace-popup-visible "false") (defvar workspaces-visible "false") - ;; (defpoll bluetooth :interval "3s" :initial "{}" "${pkgs.bt-launcher}/bin/bt-launcher status") + (defvar volume-mixer-visible "false") + (defvar mixer-sliders "") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Widgets ;; @@ -464,7 +477,7 @@ in :style "background-image: url(\"${launcher-icon}\")"))) ;; Generic slider widget ;; - (defwidget sys_slider [?header icon ?settings-icon level ?onchange ?settings-onclick ?icon-onclick ?class ?font-icon ?min] + (defwidget sys_slider [?header ?icon ?app_icon ?settings-icon level ?onchange ?settings-onclick ?icon-onclick ?class ?font-icon ?min] (box :orientation "v" :class "qs-slider" :spacing 10 @@ -483,9 +496,14 @@ in :onclick icon-onclick :hexpand false :class "icon_settings" - (box :class "icon" - :hexpand false - :style "background-image: url(\"''${icon}\")")) + (overlay + (box :class "icon" + :hexpand false + :style "background-image: url(\"''${icon}\"); opacity: ''${app_icon != "" ? "0" : "1"}") + (box :class "icon" + :hexpand false + :style "background-image: url(\"''${app_icon}\"); opacity: ''${level == 0 || level == min ? "0.5" : "1"}") + )) (label :class "icon" :visible {font-icon != "" ? "true" : "false"} :text font-icon) (eventbox :valign "CENTER" @@ -496,8 +514,9 @@ in :orientation "h" :halign "fill" :value level + :round-digits 0 :onchange onchange - :max 101 + :max 100 :min { min ?: 0 })) (eventbox :visible { settings-onclick != "" && settings-onclick != "null" ? "true" : "false" } @@ -505,7 +524,8 @@ in :class "settings" (box :class "icon" :hexpand false - :style "background-image: url(\"''${settings-icon}\")"))))) + :style "background-image: url(\"''${settings-icon}\")"))) + (children))) (defwidget sys_sliders [] (box @@ -515,17 +535,51 @@ in :space-evenly false (sys_slider :header "Volume" - :icon {volume.icon} + :icon { volume.system.muted == "true" || volume.system.level == 0 ? "${volume-0-icon}" : + volume.system.level <= 25 ? "${volume-1-icon}" : + volume.system.level <= 75 ? "${volume-2-icon}" : "${volume-3-icon}" } :icon-onclick "${eww-volume}/bin/eww-volume mute &" :settings-icon "${arrow-right-icon}" - :level { volume.muted == "true" ? "0" : volume.level } - :onchange "PULSE_SERVER=audio-vm:${toString pulseaudioTcpControlPort} ${pkgs.pamixer}/bin/pamixer --unmute --set-volume {} &") + :settings-onclick {volume-mixer-visible == "false" ? "''${EWW_CMD} update volume-mixer-visible=true &" : "''${EWW_CMD} update volume-mixer-visible=false &"} + :level { volume.system.muted == "true" ? "0" : volume.system.level } + :onchange "PULSE_SERVER=audio-vm:${toString pulseaudioTcpControlPort} ${pkgs.pamixer}/bin/pamixer --unmute --set-volume {} &" + (revealer + :transition "slidedown" + :duration "250ms" + :reveal volume-mixer-visible + (box :orientation "v" + (label :text "No audio streams" :visible {arraylength(volume.sinkInputs) == 0}) + (volume_mixer) + ))) (sys_slider :header "Brightness" :level {brightness.screen.level} :icon {brightness.icon} :min "5" - :onchange "${eww-brightness}/bin/eww-brightness set_screen {} &"))) + :onchange "${eww-brightness}/bin/eww-brightness set_screen {} &") + ) + ) + + (defwidget volume_mixer [] + (scroll + :hscroll "false" + :vscroll "true" + :height { 50 * min(3,arraylength(volume.sinkInputs)) } + (box :orientation "v" :space-evenly false + (for entry in {volume.sinkInputs} + (sys_slider + :header {entry.name} + :level {entry.muted == "true" ? "0" : entry.level} + :app_icon {entry.icon_name != "" ? "${iconThemeBase}/24x24/apps/''${entry.icon_name}.svg" : ""} + :icon { entry.muted == "true" || entry.level == 0 ? "${volume-0-icon}" : + entry.level <= 25 ? "${volume-1-icon}" : + entry.level <= 75 ? "${volume-2-icon}" : "${volume-3-icon}" } + :onchange "${pkgs.pulseaudio}/bin/pactl -s audio-vm:${toString pulseaudioTcpControlPort} set-sink-input-volume ''${entry.id} {}% & ${pkgs.pulseaudio}/bin/pactl -s audio-vm:${toString pulseaudioTcpControlPort} set-sink-input-mute ''${entry.id} 0 &" + :icon-onclick "${pkgs.pulseaudio}/bin/pactl -s audio-vm:${toString pulseaudioTcpControlPort} set-sink-input-mute ''${entry.id} toggle &") + ) + ) + ) + ) ;; Generic Widget Buttons For Quick Settings ;; (defwidget widget_button [icon ?title ?header ?subtitle ?onclick ?font-icon ?class] @@ -611,7 +665,7 @@ in (widget_button :icon "${bluetooth-1-icon}" :header "Bluetooth" - :onclick "''${EWW_CMD} close quick-settings closer & ${pkgs.bt-launcher}/bin/bt-launcher &") + :onclick "''${EWW_CMD} close quick-settings closer & ''${EWW_CMD} update volume-mixer-visible=false & ${pkgs.bt-launcher}/bin/bt-launcher &") (box :hexpand true :vexpand true @@ -635,37 +689,42 @@ in (widget_button :icon "${settings-icon}" :header "Settings" - :onclick "''${EWW_CMD} close quick-settings closer & ${pkgs.ctrl-panel}/bin/ctrl-panel >/dev/null &"))) + :onclick "''${EWW_CMD} close quick-settings closer & ''${EWW_CMD} update volume-mixer-visible=false & ${pkgs.ctrl-panel}/bin/ctrl-panel >/dev/null &"))) ''} + ;; Reusable Widgets ;; + (defwidget wrapper [?space-evenly ?spacing ?orientation] + (box + :class "wrapper_widget" + :space-evenly {space-evenly != "" ? space-evenly : "false"} + :spacing {spacing != "" ? spacing : "10"} + :orientation {orientation != "" ? orientation : "v"} + (children))) + + (defwidget desktop-widget [?space-evenly ?orientation] + (box + :class "floating-widget" + :space-evenly {space-evenly != "" ? space-evenly : "false"} + :orientation {orientation != "" ? orientation : "v"} + (children))) + ;; Quick Settings Widget ;; (defwidget quick-settings-widget [] - (box :class "floating-widget" - :orientation "v" - :space-evenly false - (box - :class "wrapper_widget" - :space-evenly false - :spacing 10 - :orientation "v" + (desktop-widget + (wrapper (etc) (sys_sliders)))) ;; Power Menu Widget ;; (defwidget power-menu-widget [] - (box :class "floating-widget" - :orientation "v" - :space-evenly false - (box - :class "wrapper_widget" - :space-evenly false - :orientation "v" + (desktop-widget + (wrapper (power_menu)))) ;; Brightness Popup Widget ;; (defwidget brightness-popup [] (revealer :transition "crossfade" :duration "200ms" :reveal brightness-popup-visible :active false - (box :class "wrapper_widget" + (wrapper (box :class "hotkey" (sys_slider :valign "center" @@ -675,17 +734,19 @@ in ;; Volume Popup Widget ;; (defwidget volume-popup [] (revealer :transition "crossfade" :duration "200ms" :reveal volume-popup-visible :active false - (box :class "wrapper_widget" + (wrapper (box :class "hotkey" (sys_slider :valign "center" - :icon {volume.icon} - :level {volume.level}))))) + :icon { volume.system.muted == "true" || volume.system.level == 0 ? "${volume-0-icon}" : + volume.system.level <= 25 ? "${volume-1-icon}" : + volume.system.level <= 75 ? "${volume-2-icon}" : "${volume-3-icon}" } + :level {volume.system.level}))))) ;; Workspace Popup Widget ;; (defwidget workspace-popup [] (revealer :transition "crossfade" :duration "200ms" :reveal workspace-popup-visible :active false - (box :class "wrapper_widget" + (wrapper (box :class "hotkey" (label :text "Desktop ''${workspace}"))))) @@ -693,7 +754,7 @@ in (defwidget quick-settings-button [screen bat-icon vol-icon bright-icon] (button :class "icon_button" :onclick "if ''${EWW_CMD} active-windows | grep -q 'quick-settings'; then \ - ''${EWW_CMD} close closer quick-settings & \ + ''${EWW_CMD} close closer quick-settings & ''${EWW_CMD} update volume-mixer-visible=false & \ else \ ''${EWW_CMD} close power-menu calendar & \ ''${EWW_CMD} open --screen \"''${screen}\" closer --arg window=\"quick-settings\" && ''${EWW_CMD} open --screen \"''${screen}\" quick-settings; \ @@ -721,6 +782,7 @@ in ''${EWW_CMD} close closer power-menu & \ else \ ''${EWW_CMD} close quick-settings calendar & \ + ''${EWW_CMD} update volume-mixer-visible=false & \ ''${EWW_CMD} open --screen \"''${screen}\" closer --arg window=\"power-menu\" && ''${EWW_CMD} open --screen \"''${screen}\" power-menu; \ fi &" (box :class "icon" @@ -740,7 +802,9 @@ in :class "control" (quick-settings-button :screen screen :bright-icon {brightness.icon} - :vol-icon {volume.icon} + :vol-icon { volume.system.muted == "true" || volume.system.level == 0 ? "${volume-0-icon}" : + volume.system.level <= 25 ? "${volume-1-icon}" : + volume.system.level <= 75 ? "${volume-2-icon}" : "${volume-3-icon}" } :bat-icon {battery.icon}))) ;; Divider ;; @@ -773,14 +837,15 @@ in ''${EWW_CMD} close closer calendar & \ else \ ''${EWW_CMD} close quick-settings power-menu & \ + ''${EWW_CMD} update volume-mixer-visible=false & \ ''${EWW_CMD} open --screen \"''${screen}\" closer --arg window=\"calendar\" && ''${EWW_CMD} open --screen \"''${screen}\" calendar; \ fi &" :class "icon_button date" "''${formattime(EWW_TIME, "%a %b %-d")}")) ;; Calendar ;; (defwidget cal [] - (box :class "floating-widget" - (box :class "wrapper_widget" + (desktop-widget + (wrapper (calendar :class "cal" :show-week-numbers false :day calendar_day @@ -870,7 +935,7 @@ in :geometry (geometry :x "0px" :y "0px" - :height "36px" + :height "30px" :width "100%" :anchor "top center") :focusable "false" @@ -954,9 +1019,10 @@ in $bg-secondary: #2B2B2B; $text-base: #FFFFFF; $text-disabled: #9c9c9c; - $text-success: #5AC379; + $text-success: rgba(90, 195, 121, 1); $icon-subdued: #3D3D3D; - $stroke-success: #5AC379; + $stroke-success: rgba(90, 195, 121, 1); + $stroke-success-muted: rgba(90, 195, 121, 0.75); $font-bold: 600; @mixin unset($rec: false) { @@ -1041,7 +1107,7 @@ in } } - @mixin slider($slider-width: 225px, $slider-height: 2px, $thumb: true, $thumb-width: 1em, $focusable: true, $radius: 7px, $shadows: true, $trough-bg: $widget-hover) { + @mixin slider($slider-width: 225px, $slider-height: 2px, $thumb: true, $thumb-width: 1em, $focusable: true, $radius: 7px, $shadows: true, $trough-bg: $widget-hover, $trough-fg: $stroke-success) { trough { border-radius: $radius; border: 0; @@ -1052,7 +1118,7 @@ in highlight, progress { - background-color: $stroke-success; + background-color: $trough-fg; border-radius: $radius; } } @@ -1197,7 +1263,7 @@ in .floating-widget { @include floating_widget; } - .qs-slider { + .qs-slider { @include unset($rec: true); @include sys-sliders; @include qs-widget($min-height: 0px); diff --git a/modules/microvm/virtualization/microvm/guivm.nix b/modules/microvm/virtualization/microvm/guivm.nix index 9dd1759d5..9c00a2011 100644 --- a/modules/microvm/virtualization/microvm/guivm.nix +++ b/modules/microvm/virtualization/microvm/guivm.nix @@ -198,6 +198,10 @@ let pkgs.pamixer pkgs.eww pkgs.wlr-randr + pkgs.pulseaudio + pkgs.pamixer + pkgs.gnused + pkgs.jq ] ++ [ pkgs.ctrl-panel ] ++ (lib.optional ( @@ -277,7 +281,7 @@ let systemd.packages = [ pkgs.blueman ]; systemd.user.services.audio-control = { - enable = true; + enable = false; description = "Audio Control application"; serviceConfig = {