nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at python-updates 263 lines 9.8 kB view raw
1# shellcheck shell=bash 2fixupOutputHooks+=('convertDesktopFiles $prefix') 3 4# Get a param out of a desktop file. First parameter is the file and the second 5# is the key who's value we should fetch. 6getDesktopParam() { 7 local file="$1" 8 local key="$2" 9 local line k v 10 11 while read -r line; do 12 if [[ "$line" = *=* ]]; then 13 k="${line%%=*}" 14 v="${line#*=}" 15 16 if [[ "$k" = "$key" ]]; then 17 echo "$v" 18 return 19 fi 20 fi 21 done < "$file" 22 23 return 1 24} 25 26# Convert a freedesktop.org icon theme for a given app to a .icns file. When possible, missing 27# icons are synthesized from SVG or rescaled from existing ones (when within the size threshold). 28convertIconTheme() { 29 local -r out=$1 30 local -r sharePath=$2 31 local -r iconName=$3 32 local -r theme=${4:-hicolor} 33 34 # Sizes based on archived Apple documentation: 35 # https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/app-icon#app-icon-sizes 36 local -ra iconSizes=(16 32 128 256 512) 37 local -ra scales=([1]="" [2]="@2") 38 39 # Based loosely on the algorithm at: 40 # https://specifications.freedesktop.org/icon-theme-spec/latest/#icon_lookup 41 # Assumes threshold = 2 for ease of implementation. 42 function findIcon() { 43 local -r iconSize=$1 44 local -r scale=$2 45 46 local scaleSuffix=${scales[$scale]} 47 local exactSize=${iconSize}x${iconSize}${scaleSuffix} 48 49 local -a validSizes=( 50 ${exactSize} 51 $((iconSize + 1))x$((iconSize + 1))${scaleSuffix} 52 $((iconSize + 2))x$((iconSize + 2))${scaleSuffix} 53 $((iconSize - 1))x$((iconSize - 1))${scaleSuffix} 54 $((iconSize - 2))x$((iconSize - 2))${scaleSuffix} 55 ) 56 57 local fallbackIcon= 58 59 for iconIndex in "${!candidateIcons[@]}"; do 60 for maybeSize in "${validSizes[@]}"; do 61 icon=${candidateIcons[$iconIndex]} 62 if [[ $icon = */$maybeSize/* ]]; then 63 if [[ $maybeSize = $exactSize ]]; then 64 echo "fixed $icon" 65 return 0 66 else 67 echo "threshold $icon" 68 return 0 69 fi 70 elif [[ -a $icon && -z "$fallbackIcon" ]]; then 71 fallbackIcon="$icon" 72 fi 73 done 74 done 75 76 if [[ -n "$fallbackIcon" ]]; then 77 echo "fallback $fallbackIcon" 78 return 0 79 fi 80 81 echo "scalable" 82 } 83 84 function resizeIcon() { 85 local -r in=$1 86 local -r out=$2 87 local -r iconSize=$3 88 local -r scale=$4 89 90 local density=$((72 * scale))x$((72 * scale)) 91 local dim=$((iconSize * scale)) 92 93 echo "desktopToDarwinBundle: resizing icon $in to $out, size $dim" >&2 94 magick convert -scale "${dim}x${dim}" -density "$density" -units PixelsPerInch "$in" "$out" 95 convertIfUnsupportedIcon "$out" "$iconSize" "$scale" 96 } 97 98 function synthesizeIcon() { 99 local -r in=$1 100 local -r out=$2 101 local -r iconSize=$3 102 local -r scale=$4 103 104 if [[ $in != '-' ]]; then 105 local density=$((72 * scale))x$((72 * scale)) 106 local dim=$((iconSize * scale)) 107 108 echo "desktopToDarwinBundle: rasterizing svg $in to $out, size $dim" >&2 109 rsvg-convert --keep-aspect-ratio --width "$dim" --height "$dim" "$in" --output "$out" 110 magick convert -density "$density" -units PixelsPerInch "$out" "$out" 111 convertIfUnsupportedIcon "$out" "$iconSize" "$scale" 112 else 113 return 1 114 fi 115 } 116 117 # macOS does not correctly display 16x and 32x png icons on app bundles 118 # they need to be converted to argb 119 function convertIfUnsupportedIcon() { 120 local -r in=$1 121 local -r iconSize=$2 122 local -r scale=$3 123 local -r out=${in%.png}.argb 124 125 if [[ ($scale -eq 1) && ($iconSize -eq 32 || $iconSize -eq 16) ]]; then 126 echo "desktopToDarwinBundle: converting ${iconSize}x icon to argb" >&2 127 icnsutil convert "$out" "$in" 128 rm "$in" 129 fi 130 } 131 132 function getIcons() { 133 local -r sharePath=$1 134 local -r iconname=$2 135 local -r theme=$3 136 local -r resultdir=$(mktemp -d) 137 138 local -ar candidateIcons=( 139 "${sharePath}/icons/${theme}/"*"/${iconname}.png" 140 "${sharePath}/icons/${theme}/"*"/${iconname}.xpm" 141 ) 142 143 local -a scalableIcon=("${sharePath}/icons/${theme}/scalable/${iconname}.svg"*) 144 if [[ ${#scalableIcon[@]} = 0 ]]; then 145 scalableIcon=('-') 146 fi 147 148 # Tri-state variable, NONE means no icons have been found, an empty 149 # icns file will be generated, not sure that's necessary because macOS 150 # will default to a generic icon if no icon can be found. 151 # 152 # OTHER means an appropriate icon was found. 153 # 154 # Any other value is a path to an icon file that isn't scalable or 155 # within the threshold. This is used as a fallback in case no better 156 # icon can be found and will be scaled as much as 157 # necessary to result in appropriate icon sizes. 158 local foundIcon=NONE 159 for iconSize in "${iconSizes[@]}"; do 160 for scale in "${!scales[@]}"; do 161 local iconResult=$(findIcon $iconSize $scale) 162 local type=${iconResult%% *} 163 local icon=${iconResult#* } 164 local scaleSuffix=${scales[$scale]} 165 local result=${resultdir}/${iconSize}x${iconSize}${scales[$scale]}${scaleSuffix:+x}.png 166 echo "desktopToDarwinBundle: using $type icon $icon for size $iconSize$scaleSuffix" >&2 167 case $type in 168 fixed) 169 local density=$((72 * scale))x$((72 * scale)) 170 magick convert -density "$density" -units PixelsPerInch "$icon" "$result" 171 convertIfUnsupportedIcon "$result" "$iconSize" "$scale" 172 foundIcon=OTHER 173 ;; 174 threshold) 175 # Synthesize an icon of the exact size if a scalable icon is available 176 # instead of scaling one and ending up with a fuzzy icon. 177 if ! synthesizeIcon "${scalableIcon[0]}" "$result" "$iconSize" "$scale"; then 178 resizeIcon "$icon" "$result" "$iconSize" "$scale" 179 fi 180 foundIcon=OTHER 181 ;; 182 scalable) 183 synthesizeIcon "${scalableIcon[0]}" "$result" "$iconSize" "$scale" || true 184 foundIcon=OTHER 185 ;; 186 fallback) 187 # Use the largest size available to scale to 188 # appropriate sizes. 189 if [[ $foundIcon != OTHER ]]; then 190 foundIcon=$icon 191 fi 192 ;; 193 *) 194 ;; 195 esac 196 done 197 done 198 if [[ $foundIcon != NONE && $foundIcon != OTHER ]]; then 199 # Ideally we'd only resize to whatever the closest sizes are, 200 # starting from whatever icon sizes are available. 201 for iconSize in 16 32 128 256 512; do 202 local result=${resultdir}/${iconSize}x${iconSize}.png 203 resizeIcon "$foundIcon" "$result" "$iconSize" 1 204 done 205 fi 206 echo "$resultdir" 207 } 208 209 iconsdir=$(getIcons "$sharePath" "apps/${iconName}" "$theme") 210 if [[ -n "$(ls -A1 "$iconsdir")" ]]; then 211 icnsutil compose --toc "$out/${iconName}.icns" "$iconsdir/"* 212 else 213 echo "Warning: no icons were found. Creating an empty icon for ${iconName}.icns." 214 touch "$out/${iconName}.icns" 215 fi 216} 217 218processExecFieldCodes() { 219 local -r file=$1 220 local -r execRaw=$(getDesktopParam "${file}" "Exec") 221 local -r execNoK="${execRaw/\%k/${file}}" 222 local -r execNoKC="${execNoK/\%c/$(getDesktopParam "${file}" "Name")}" 223 local -r icon=$(getDesktopParam "${file}" "Icon") 224 local -r execNoKCI="${execNoKC/\%i/${icon:+--icon }${icon}}" 225 local -r execNoKCIfu="${execNoKCI/ \%[fu]/}" 226 local -r exec="${execNoKCIfu/ \%[FU]/}" 227 if [[ "$exec" != "$execRaw" ]]; then 228 echo 1>&2 "desktopToDarwinBundle: Application bundles do not understand desktop entry field codes. Changed '$execRaw' to '$exec'." 229 fi 230 echo "$exec" 231} 232 233# For a given .desktop file, generate a darwin '.app' bundle for it. 234convertDesktopFile() { 235 local -r file=$1 236 local -r sharePath=$(dirname "$(dirname "$file")") 237 local -r name=$(getDesktopParam "${file}" "Name") 238 local -r macOSExec=$(getDesktopParam "${file}" "X-macOS-Exec") 239 if [[ "$macOSExec" ]]; then 240 local -r exec="$macOSExec" 241 else 242 local -r exec=$(processExecFieldCodes "${file}") 243 fi 244 local -r iconName=$(getDesktopParam "${file}" "Icon") 245 local -r squircle=$(getDesktopParam "${file}" "X-macOS-SquircleIcon") 246 247 mkdir -p "${!outputBin}/Applications/${name}.app/Contents/MacOS" 248 mkdir -p "${!outputBin}/Applications/${name}.app/Contents/Resources" 249 250 convertIconTheme "${!outputBin}/Applications/${name}.app/Contents/Resources" "$sharePath" "$iconName" 251 252 write-darwin-bundle "${!outputBin}" "$name" "$exec" "$iconName" "$squircle" 253} 254 255convertDesktopFiles() { 256 local dir="$1/share/applications/" 257 258 if [ -d "${dir}" ]; then 259 for desktopFile in $(find "$dir" -iname "*.desktop"); do 260 convertDesktopFile "$desktopFile"; 261 done 262 fi 263}