Skip to content

Commit

Permalink
Desktop: Automatic scaling on HiDPI displays
Browse files Browse the repository at this point in the history
- Enable automatic scaling for HiDPI display
- Scaling is performed during boot and on display events
- Manual scaling settings not supported
- Minor adjustments to taskbar audio device widgets naming and style

Signed-off-by: Kajus Naujokaitis <[email protected]>
  • Loading branch information
kajusnau committed Jan 23, 2025
1 parent 20f71cc commit e414cff
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 43 deletions.
4 changes: 0 additions & 4 deletions modules/desktop/graphics/ewwbar.nix
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,6 @@ in
mode = "0644";
};

services.udev.extraRules = ''
ACTION=="change", SUBSYSTEM=="drm", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}+="eww-display-trigger.service"
'';

systemd.user.services = {
ewwbar = {
enable = true;
Expand Down
47 changes: 35 additions & 12 deletions modules/desktop/graphics/ewwbar/config/scripts/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ let
runtimeInputs = [
pkgs.wlr-randr
pkgs.jq
pkgs.bc
pkgs.bash
pkgs.gawk
pkgs.xorg.setxkbmap
Expand All @@ -97,17 +98,27 @@ let
${ewwCmd} daemon --force-wayland
update-vars
# Launch ewwbar for each connected display
mapfile -t displays < <(echo "$wlr_randr_output" | jq -r '.[] | select(.enabled == true) | .model')
for display_name in "''${displays[@]}"; do
echo Opening ewwbar on display "$display_name"
${ewwCmd} open --force-wayland --no-daemonize --screen "$display_name" bar --id bar:"$display_name" --arg screen="$display_name"
open-bars "$wlr_randr_output"
}
# EWW can only perform integer scaling by default
# Therefore we need to calculate the expected scaled width in pixels
open-bars() {
local wlr_randr_output=$1
echo "$wlr_randr_output" | jq -c --unbuffered '.[] | select(.enabled == true)' | while read -r display; do
scale=$(echo "$display" | jq -r '.scale')
display_name=$(echo "$display" | jq -r '.model')
width=$(echo "$display" | jq -r '.modes[] | select(.current == true) | .width')
#scaled_width=$(printf "%.2f" "$(echo "(2 / $scale) * 100" | bc -l)")
scaled_width=$(echo "$width / $scale" | bc -l | cut -d'.' -f1)
${ewwCmd} open --force-wayland --no-daemonize --screen "$display_name" bar --id bar:"$display_name" --arg screen="$display_name" --arg width="$scaled_width"
done
}
# Reloads current config without opening new windows
reload() {
${ewwCmd} reload
sleep 0.5
update-vars
}
Expand Down Expand Up @@ -270,7 +281,8 @@ let
volume_percentage: (
(.volume | to_entries | first | .value.value_percent | sub("%$"; "")) // "0"
),
is_muted: (.mute // false)
is_muted: (.mute // false),
device_type: (.active_port as $active_port | .ports[] | select(.name == $active_port).type // "")
}
)
catch halt
Expand All @@ -292,7 +304,8 @@ let
volume_percentage: (
(.volume | to_entries | first | .value.value_percent | sub("%$"; "")) // "0"
),
is_muted: (.mute // false)
is_muted: (.mute // false),
device_type: (.active_port as $active_port | .ports[] | select(.name == $active_port).type // "")
}
)
catch halt
Expand Down Expand Up @@ -334,7 +347,7 @@ let
state: (.state // ""),
friendly_name: (.description // ""),
is_muted: (.mute // false),
device_type: (.active_port as $active_port | .ports[] | select(.name == $active_port).type // "") | ascii_downcase
device_type: (.active_port as $active_port | .ports[] | select(.name == $active_port).type // "")
}
]
) catch halt
Expand All @@ -358,7 +371,7 @@ let
),
state: (.state // ""),
friendly_name: (.description // ""),
device_type: (.active_port as $active_port | .ports[] | select(.name == $active_port).type // "") | ascii_downcase
device_type: (.active_port as $active_port | .ports[] | select(.name == $active_port).type // "")
}
]
) catch halt
Expand Down Expand Up @@ -514,6 +527,7 @@ let
runtimeInputs = [
pkgs.wlr-randr
pkgs.jq
pkgs.bc
pkgs.inotify-tools
];
bashOptions = [ ];
Expand All @@ -523,7 +537,11 @@ let
open_bar() {
local display_name=$1
${ewwCmd} open --force-wayland --no-daemonize --screen "$display_name" bar --id bar:"$display_name" --arg screen="$display_name"
local scale=$2
local width=$3
#scaled_width=$(printf "%.2f" "$(echo "(2 / $scale) * 100" | bc -l)")
scaled_width=$(echo "$width / $scale" | bc -l | cut -d'.' -f1)
${ewwCmd} open --force-wayland --no-daemonize --screen "$display_name" bar --id bar:"$display_name" --arg screen="$display_name" --arg width="$scaled_width"
}
close_bar() {
Expand All @@ -539,12 +557,17 @@ let
wlr_randr_output=$(wlr-randr --json)
current_displays=$(echo "$wlr_randr_output" | jq 'length')
mapfile -t current_display_names < <(echo "$wlr_randr_output" | jq -r '.[] | select(.enabled == true) | .model')
mapfile -t current_scales < <(echo "$wlr_randr_output" | jq -r '.[] | select(.enabled == true) | .scale')
mapfile -t current_widths < <(echo "$wlr_randr_output" | jq -r '.[] | select(.enabled == true) | .modes[] | select(.current == true) | .width')
if (( current_displays > prev_displays )); then
# Open bars for added displays
mapfile -t added_displays < <(comm -13 <(printf "%s\n" "''${prev_display_names[@]}" | sort) <(printf "%s\n" "''${current_display_names[@]}" | sort))
for display_name in "''${added_displays[@]}"; do
open_bar "$display_name"
for i in "''${!added_displays[@]}"; do
display_name="''${added_displays[$i]}"
scale="''${current_scales[$i]}"
width="''${current_widths[$i]}"
open_bar "$display_name" "$scale" "$width"
done
elif (( current_displays < prev_displays )); then
# Close bars for removed displays
Expand Down
22 changes: 13 additions & 9 deletions modules/desktop/graphics/ewwbar/config/widgets/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ writeText "widgets.yuck" ''
:space-evenly false
(slider_with_children
:class "qs-slider"
:header-left {audio_output.friendly_name =~ '.*sof-hda-dsp.*' ? "Built-in Speaker" :
:header-left {audio_output.friendly_name =~ '.*sof-hda-dsp.*' ? "Built-in ''${audio_output.device_type}" :
audio_output.friendly_name}
:header-onclick "''${EWW_CMD} update audio_output_selector_visible=''${!audio_output_selector_visible} &"
:child_0_visible audio_output_selector_visible
Expand All @@ -227,7 +227,7 @@ writeText "widgets.yuck" ''
(slider_with_children
:visible { audio_input.state == "RUNNING" }
:class "qs-slider"
:header-left {audio_input.friendly_name =~ '.*sof-hda-dsp.*' ? "Built-in Microphone" :
:header-left {audio_input.friendly_name =~ '.*sof-hda-dsp.*' ? "Built-in ''${audio_input.device_type}" :
audio_input.friendly_name }
:header-onclick "''${EWW_CMD} update audio_input_selector_visible=''${!audio_input_selector_visible} &"
:child_0_visible audio_input_selector_visible
Expand Down Expand Up @@ -268,8 +268,10 @@ writeText "widgets.yuck" ''
:class "default_button"
:onclick "${ewwScripts.eww-audio}/bin/eww-audio set_default_sink ''${device.id} && ''${EWW_CMD} update audio_output_selector_visible=false &"
(box :orientation "h" :spacing 10 :space-evenly "false"
(image :halign "start" :icon {device.device_type})
(label :halign "start" :limit-width 30 :text { device.friendly_name =~ '.*sof-hda-dsp.*' ? "Built-in Speaker" : device.friendly_name })
(image :halign "start" :path { device.is_muted == "true" || device.volume_percentage == 0 ? "${pkgs.ghaf-artwork}/icons/volume-0.svg" :
device.volume_percentage <= 25 ? "${pkgs.ghaf-artwork}/icons/volume-1.svg" :
device.volume_percentage <= 75 ? "${pkgs.ghaf-artwork}/icons/volume-2.svg" : "${pkgs.ghaf-artwork}/icons/volume-3.svg" })
(label :halign "start" :limit-width 30 :text { device.friendly_name =~ '.*sof-hda-dsp.*' ? "Built-in ''${device.device_type}" : device.friendly_name })
(image :halign "start" :icon "emblem-ok" :style "opacity: ''${device.is_default ? "1" : "0"}")
)
)
Expand All @@ -289,8 +291,10 @@ writeText "widgets.yuck" ''
:class "default_button"
:onclick "${ewwScripts.eww-audio}/bin/eww-audio set_default_source ''${device.id} && ''${EWW_CMD} update audio_input_selector_visible=false &"
(box :orientation "h" :spacing 10 :space-evenly "false"
(image :halign "start" :icon {device.device_type == "mic" ? "microphone" : device.device_type})
(label :halign "start" :limit-width 30 :text { device.friendly_name =~ '.*sof-hda-dsp.*' ? "Built-in Microphone" : device.friendly_name })
(image :halign "start" :icon { device.is_muted == "true" || device.volume_percentage == 0 ? "microphone-sensitivity-muted" :
device.volume_percentage <= 25 ? "microphone-sensitivity-low" :
device.volume_percentage <= 75 ? "microphone-sensitivity-medium" : "microphone-sensitivity-high" })
(label :halign "start" :limit-width 30 :text { device.friendly_name =~ '.*sof-hda-dsp.*' ? "Built-in ''${device.device_type}" : device.friendly_name })
(image :halign "start" :icon "emblem-ok" :style "opacity: ''${device.is_default ? "1" : "0"}")
)
)
Expand Down Expand Up @@ -480,9 +484,9 @@ writeText "widgets.yuck" ''
(slider
:valign "center"
:image { audio_output.is_muted == "true" || audio_output.volume_percentage == 0 ? "${pkgs.ghaf-artwork}/icons/volume-0.svg" :
audio_output.volume_percentage <= 25 ? "${pkgs.ghaf-artwork}/icons/volume-1.svg" :
audio_output.volume_percentage <= 75 ? "${pkgs.ghaf-artwork}/icons/volume-2.svg" : "${pkgs.ghaf-artwork}/icons/volume-3.svg" }
:level {audio_output.volume_percentage}))))
audio_output.volume_percentage <= 25 ? "${pkgs.ghaf-artwork}/icons/volume-1.svg" :
audio_output.volume_percentage <= 75 ? "${pkgs.ghaf-artwork}/icons/volume-2.svg" : "${pkgs.ghaf-artwork}/icons/volume-3.svg" }
:level { audio_output.is_muted == "true" ? "0" : audio_output.volume_percentage }))))
;; Workspace Popup Widget ;;
(defwidget workspace-popup []
Expand Down
10 changes: 5 additions & 5 deletions modules/desktop/graphics/ewwbar/config/windows/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
}:
writeText "windows.yuck" ''
;; Bar Window ;;
(defwindow bar [screen]
(defwindow bar [screen ?width]
:geometry (geometry
:x "0px"
:y "0px"
:height "28px"
:width "100%"
:width {width ?: "100%"}
:anchor "top center")
:focusable "false"
:hexpand "false"
Expand Down Expand Up @@ -53,7 +53,7 @@ writeText "windows.yuck" ''
;; Volume Popup Window ;;
(defwindow volume-popup
:monitor 0
:geometry (geometry :y "150px"
:geometry (geometry :y "10%"
:x "0px"
:anchor "bottom center")
:stacking "overlay"
Expand All @@ -62,7 +62,7 @@ writeText "windows.yuck" ''
;; Brightness Popup Window ;;
(defwindow brightness-popup
:monitor 0
:geometry (geometry :y "150px"
:geometry (geometry :y "10%"
:x "0px"
:anchor "bottom center")
:stacking "overlay"
Expand All @@ -71,7 +71,7 @@ writeText "windows.yuck" ''
;; Workspace Popup Window ;;
(defwindow workspace-popup
:monitor 0
:geometry (geometry :y "150px"
:geometry (geometry :y "10%"
:x "0px"
:anchor "bottom center")
:stacking "overlay"
Expand Down
95 changes: 87 additions & 8 deletions modules/desktop/graphics/labwc.config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,73 @@ let

text = "labwc -C /etc/labwc -s labwc-autostart >/tmp/session.labwc.log 2>&1";
};

scale-displays = pkgs.writeShellApplication {
name = "scale-displays";
runtimeInputs = [
pkgs.wlr-randr
pkgs.jq
pkgs.gawk
pkgs.bc
pkgs.systemd
];
bashOptions = [ ];
text = ''
wlr-randr --json | jq -c --unbuffered '.[] | select(.enabled == true)' | while read -r display; do
# Extract necessary details from JSON
name=$(echo "$display" | jq -r '.name')
width_mm=$(echo "$display" | jq -r '.physical_size.width')
height_mm=$(echo "$display" | jq -r '.physical_size.height')
mode=$(echo "$display" | jq -c '.modes[] | select(.current == true)')
width_px=$(echo "$mode" | jq -r '.width')
height_px=$(echo "$mode" | jq -r '.height')
# Validate extracted values
if [[ -z "$name" || -z "$width_mm" || -z "$height_mm" || -z "$width_px" || -z "$height_px" ]]; then
echo "Error: Missing data for display $name. Skipping."
continue
fi
# Convert physical dimensions to inches
width_in=$(echo "$width_mm" | awk '{print $1 / 25.4}')
height_in=$(echo "$height_mm" | awk '{print $1 / 25.4}')
diagonal_px=$(echo "$width_px $height_px" | awk '{print sqrt($1^2 + $2^2)}')
diagonal_in=$(echo "$width_in $height_in" | awk '{print sqrt($1^2 + $2^2)}')
ppi=$(echo "$diagonal_px $diagonal_in" | awk '{print $1 / $2}')
# Determine scaling factor based on PPI
if (( $(echo "$ppi >= 0 && $ppi < 120" | bc -l) )); then
scale=1 # 100% scaling for PPI lower than 120
elif (( $(echo "$ppi >= 120 && $ppi < 170" | bc -l) )); then
scale=1.25 # 125% scaling for PPI between 120 and 170
elif (( $(echo "$ppi >= 170 && $ppi < 200" | bc -l) )); then
scale=1.50 # 150% scaling for PPI between 170 and 200
elif (( $(echo "$ppi >= 200 && $ppi < 300" | bc -l) )); then
scale=1.50 # 200% scaling for PPI between 200 and 300
else
scale=2 # Default to 200% scaling for PPI above 300
fi
# Apply scaling using wlr-randr
echo "Display $name calculated PPI: $ppi"
echo "Setting scaling for display $name to $scale"
wlr-randr --output "$name" --scale "$scale"
done
'';
};

display-event-trigger = pkgs.writeShellApplication {
name = "display-event-trigger";
runtimeInputs = [ ];
bashOptions = [ ];
text = ''
# Run the following commands in order every time a display change event is detected
${scale-displays}/bin/scale-displays # Auto scaling
echo 1 > ~/.config/eww/display # Inform EWW to check for new displays
${pkgs.mako}/bin/makoctl set-mode default # Reset mako mode so notifications don't break
'';
};
in
{
config = lib.mkIf cfg.enable {
Expand Down Expand Up @@ -339,7 +406,7 @@ in
};

services.udev.extraRules = ''
ACTION=="change", SUBSYSTEM=="drm", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}+="mako-reset.service"
ACTION=="change", SUBSYSTEM=="drm", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}+="display-change-trigger.service"
'';

systemd.user.services = {
Expand Down Expand Up @@ -369,13 +436,6 @@ in
wantedBy = [ "ghaf-session.target" ];
};

mako-reset = {
enable = true;
serviceConfig = {
ExecStart = "${pkgs.mako}/bin/makoctl set-mode default";
};
};

mako = {
enable = true;
description = "Notification daemon";
Expand Down Expand Up @@ -454,6 +514,25 @@ in
partOf = [ "ghaf-session.target" ];
wantedBy = [ "ghaf-session.target" ];
};

hidpi-auto-scaling = {
description = "HiDPI scaling service at boot";
serviceConfig = {
Type = "oneshot";
ExecStart = "${scale-displays}/bin/scale-displays";
};
partOf = [ "ghaf-session.target" ];
wantedBy = [ "ghaf-session.target" ];
before = [ "ewwbar.service" "swaybg.service" ];
};

display-change-trigger = {
description = "display-change-trigger";
serviceConfig = {
Type = "oneshot";
ExecStart = "${display-event-trigger}/bin/display-event-trigger";
};
};
};
};
}
9 changes: 4 additions & 5 deletions modules/desktop/graphics/styles/ewwbar-style.nix
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,12 @@
}
slider {
border: 0 solid transparent;
border-radius: 50%;
background-image: none;
box-shadow: none;
@if $thumb {
box-shadow: none;
background-color: #D3D3D3;
background-image: none;
border: 0 solid transparent;
border-radius: 50%;
min-height: $thumb-width;
min-width: $thumb-width;
margin: -($thumb-width / 2) 0;
Expand All @@ -130,7 +130,6 @@
min-width: 0;
min-height: 0;
background-color: transparent;
background-image: none;
}
}
Expand Down

0 comments on commit e414cff

Please sign in to comment.