File indexing completed on 2024-04-28 16:49:43

0001 #compdef kscreen-doctor
0002 
0003 # SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
0004 #
0005 # SPDX-License-Identifier: GPL-2.0-or-later
0006 
0007 local curcontext="$curcontext" state expl ret=1
0008 
0009 _kscreen-doctor-check-jq() {
0010   local tag=$1 descr=$2
0011 
0012   if (( $+commands[jq] )); then
0013     return 0
0014   else
0015     local -a empty  # Is there a better way to print description?
0016     _describe -t "$tag" "$descr (completions unavailable, please install jq)" empty
0017     return 1
0018   fi
0019 }
0020 
0021 _kscreen-doctor-outputs() {
0022   local -a outputs  # array of triples: id, name, enabled status (true/false)
0023 
0024   _kscreen-doctor-check-jq outputs output || return 1
0025 
0026   data=(${(f)"$(
0027     kscreen-doctor --json |
0028     jq --raw-output '
0029         .outputs
0030         | map(select(.connected))
0031         | sort_by((.enabled | not), .name)
0032         | map(.id, .name, .enabled)
0033         | .[]
0034       '
0035     )"})
0036 
0037   local id name enabled desc
0038   for id name enabled in $data ; do
0039     if [[ "$enabled" == true ]]; then
0040       enabled="[Enabled] "  # a bit of right padding, like git does for recent commits list
0041     else
0042       enabled="[Disabled]"
0043     fi
0044     desc="$enabled Output ID $id, connected as $name"
0045     # Duplicate completions for id and name. But given identical description,
0046     # they will occupy the same row.
0047     outputs+=( "$id:$desc" "$name:$desc" )
0048   done
0049 
0050   _describe -t outputs "output" outputs "$@" -V unsorted
0051 }
0052 
0053 _kscreen-mode-fmt() {
0054   local dst="$1" mode="$2"
0055   local size refresh_rate
0056 
0057   size=${mode%@*}
0058   refresh_rate=${mode#*@}
0059   # WWWWxHHHH @ RRR, (l:n:) is padding with spaces on the left
0060   printf -v $dst " ${(l:9:)size} @ ${(l:3:)refresh_rate} "
0061 }
0062 
0063 _kscreen-doctor-mode() {
0064   local ret=1 output="$1"
0065 
0066   _kscreen-doctor-check-jq modes mode || return $ret
0067 
0068   # 'top_comp' is a list of current and preferred mode IDs (literal completions).
0069   # 'top_descr' is a list of pretty-printed version of top_comp and their descriptions.
0070   # 'rest_comp' is a list of mode IDs, and 'rest_fmt' is pretty-printed version of rest (without descriptions).
0071   local -a stdout top_comp top_descr rest_comp rest_fmt
0072   stdout=(${(f)"$(
0073     kscreen-doctor --json |
0074     jq --raw-output --arg output "$output" '
0075         .outputs[]
0076         # note about "\(.id)": stringifying known-to-be integer sounds safer
0077         # than parsing untrusted input as an int.
0078         | select("\(.id)" == $output or .name == $output)
0079         | [
0080             .preferredModes as $pref
0081           | .currentModeId as $curr
0082           | .modes
0083           | map({
0084               id: .id,
0085               w: .size.width,
0086               h: .size.height,
0087               r: (.refreshRate | round),
0088             })
0089           # Some names may be duplicated after rounding refresh rates.
0090           # Group them by what going to be part of a name,
0091           # while keeping a list of IDs to be able to determine which ones are "current" and/or "preferred".
0092           | group_by(.w, .h, .r)
0093           | map({  # flatten back
0094               p: any(
0095                 # this is how you do a nested loop
0096                 .id as $id | $pref[] as $p | $id == $p
0097               ),
0098               c: any(.id == $curr),
0099               # Just take the first mode`s data. They are identical, and there will always be at least one.
0100               w: .[0].w,
0101               h: .[0].h,
0102               r: .[0].r
0103             })
0104           | sort_by(.w, .h, .r)
0105           | reverse
0106           | map({ name: "\(.w)x\(.h)@\(.r)", p: .p, c: .c })
0107           # show current mode on top, then preferred, then the rest.
0108           # Second line is a flag that indicates whether current mode is also the preferred one.
0109           | map(select(.c) | "\(.name);\(.p)")[],
0110             "--",
0111             map(select((.c | not) and .p) | .name)[],
0112             "++",
0113             map(select((.c | not) and (.p | not)) | .name)[]
0114         ][]
0115       '
0116     )"})
0117   # sample result:
0118   #   4096x2160@60;false
0119   #   --
0120   #   1920x1080@60
0121   #   ++
0122   #   4096x2160@50
0123   #   4096x2160@30
0124   #   3840x2160@60
0125   #   3840x2160@50
0126   #   3840x2160@30
0127   #   1920x1080@50
0128   #   1920x1080@30
0129   #   1920x1080@25
0130   #   ...
0131   # The 'false' on a first line indicates that current is not also a
0132   # preferred one. If it were, it would not appear in the list of preferred
0133   # modes below.
0134 
0135   local current label formatted line parser=current  # then "preferred" and "rest"
0136   for line in $stdout ; do
0137     case $line in
0138       --)
0139         parser=preferred
0140         continue
0141         ;;
0142       ++)
0143         parser=rest
0144         continue
0145     esac
0146     case $parser in
0147       current)
0148         current=${line%;*}
0149         _kscreen-mode-fmt formatted "$current"
0150         label="Current"
0151         # current is also preferred
0152         if [[ "${line#*;}" != "false" ]]; then
0153           label="$label & Preferred"
0154         fi
0155         top_comp+=( "$current" )
0156         top_descr+=( "${formatted}:${label} mode" )
0157         ;;
0158       preferred)
0159         _kscreen-mode-fmt formatted "$line"
0160         top_comp+=( "$line" )
0161         top_descr+=( "${formatted}:Preferred mode" )
0162         ;;
0163       rest)
0164         _kscreen-mode-fmt formatted "$line"
0165         rest_comp+=( $line )
0166         rest_fmt+=( $formatted )
0167     esac
0168   done
0169 
0170   ret=1
0171   # Resetting expl to avoid it 'leaking' from one line to the next.
0172   expl=()
0173   _describe -V -t notable-modes 'notable modes' top_descr top_comp && ret=0
0174   expl=()
0175   _wanted all-modes expl 'other available modes' \
0176     compadd -o nosort -d rest_fmt -a rest_comp \
0177     && ret=0
0178   return $ret
0179 }
0180 
0181 _kscreen-doctor-priorities() {
0182   local connected_count
0183   if (( $+commands[jq] )); then
0184     connected_count="$(
0185       kscreen-doctor --json |
0186       jq --raw-output '
0187         .outputs
0188         | map(select(.connected))
0189         | length
0190       ')"
0191   else
0192     # best effort fallback
0193     connected_count="$(kscreen-doctor --outputs | wc -l)"
0194   fi
0195   _alternative "priority::( {0..${connected_count}} )"
0196 }
0197 
0198 _arguments -C \
0199     '(-h --help)'{-h,--help}'[Displays help on commandline options]' \
0200     '--help-all[Displays help including Qt specific options]' \
0201     '(-i --info)'{-i,--info}'[Show runtime information: backends, logging, etc]' \
0202     '(-j --json)'{-j,--json}'[Show configuration in JSON format]' \
0203     '(-o --outputs)'{-o,--outputs}'[Show outputs]' \
0204     '(DPMS)'{-d=,--dpms=}'[(Wayland only) Display power management]:status:(on off)' \
0205     '--dpms-excluded=[Do not apply the dpms change to the output with said model names]:output:_kscreen-doctor-outputs' \
0206     '(-l --log)'{-l=,--log=}'[Write a comment to the log file]:comment' \
0207     '*: :->settings' && ret=0
0208 
0209 case $state in
0210   settings)
0211     if compset -P 'output.' ; then
0212 
0213       if compset -P 1 '*.' ; then
0214         local output; output="${${IPREFIX#*.}%.}"
0215 
0216         if compset -P 1 'mode.' ; then
0217           _kscreen-doctor-mode "$output" && ret=0
0218         elif compset -P 1 'priority.' ; then
0219           _kscreen-doctor-priorities && ret=0
0220         elif compset -P 1 'position.' ; then
0221           _arguments '1::x,y:' && ret=0
0222         elif compset -P 1 'rgbrange.' ; then
0223           _alternative 'rgbrange::(automatic full limited)' && ret=0
0224         elif compset -P 1 'rotation.' || compset -P 1 'orientation.' ; then
0225           _alternative 'rotation::(none normal left right inverted)' && ret=0
0226         elif compset -P 1 'overscan.' ; then
0227           local -a overscan_descr overscan_comp
0228           overscan_descr=(
0229             ' 0%:Disable overscan (Default)'
0230             ' 3%:Action safe area (Vertical, round down)'
0231             ' 4%:Action safe area (Vertical, round up)'
0232             '10%:Action safe area (Horizontal, 14:9 displayed on 16:9)'
0233             '15%:Action safe area (Horizontal,  4:3 displayed on 16:9)'
0234             '17%:Title safe area  (Horizontal,  4:3 displayed on 16:9)'
0235           )
0236           overscan_comp=('0' '3' '4' '10' '15' '17')
0237           _describe -t overscan "(Wayland only) output overscan, 0%%..100%%" \
0238             overscan_descr overscan_comp -o nosort && ret=0
0239         elif compset -P 1 'scale.' ; then
0240           local -a scale_descr scale_comp
0241           scale_descr=('100%' '125%' '150%' '175%' '200%' '225%' '250%' '275%' '300%' )
0242           scale_comp=( '1'    '1,25' '1,5'  '1,75' '2'    '2,25' '2,5'  '2,75' '3'    )
0243           _describe -t scale "(Wayland only) per-output scaling" scale_descr scale_comp -o nosort && ret=0
0244         elif compset -P 1 'vrrpolicy.' ; then
0245           _alternative 'vrrpolicy::(never always automatic)' && ret=0
0246         else
0247           # two groups: first without suffix, and second with '.' at the end
0248           _describe -t subcommands 'subcommand' '(
0249             enable:"Toggle output"
0250             disable:"Toggle output"
0251             primary:"Make this output primary (same as priority.1)"
0252           )' -- '(
0253             mode:"Resolution and refresh rate"
0254             orientation:"Display orientation"
0255             overscan:"(Wayland only) Overscan area size"
0256             position:"x and y coordinates"
0257             priority:"Set output priority"
0258             rgbrange:"RGB range"
0259             rotation:"Display orientation"
0260             scale:"(Wayland only) Per-output scaling"
0261             vrrpolicy:"(Wayland only) Variable refresh rate"
0262             )' -S '.' && ret=0
0263         fi
0264       else
0265         _kscreen-doctor-outputs -S '.' && ret=0
0266       fi
0267     else
0268       _sep_parts '(output)' . && ret=0
0269     fi
0270   ;;
0271 esac
0272 
0273 return $ret