gdctl: Add bash completion integration

This auto-completes things such as available connectors, modes, scales,
transforms, etc.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4190>
This commit is contained in:
Jonas Ådahl
2024-12-20 10:15:47 +01:00
committed by Marge Bot
parent a0c5c09e9b
commit f9bb7aa2e6
5 changed files with 122 additions and 11 deletions

View File

@ -11,6 +11,7 @@ include:
meson-options:
-Dxwayland_initfd=enabled
-Dprofiler=true
-Dbash_completion=false
build-sysext:
before_script:
@ -124,6 +125,7 @@ variables:
zenity
python3-dbusmock
gnome-desktop-testing
python3-argcomplete
ruff
FDO_DISTRIBUTION_EXEC: |
@ -194,8 +196,6 @@ variables:
mkdir -p /opt/mutter
cp build/src/tests/kvm/bzImage /opt/mutter/bzImage
dnf install -y python3-argcomplete
git clone https://github.com/arighi/virtme-ng.git
cd virtme-ng
git fetch --tags

View File

@ -378,6 +378,7 @@ have_kvm_tests = false
have_tty_tests = false
have_installed_tests = false
have_x11_tests = false
have_bash_completion = get_option('bash_completion')
if have_tests
gtk3_dep = dependency('gtk+-3.0', version: gtk3_req)
@ -744,6 +745,8 @@ summary('mandir', mandir, section: 'Directories')
summary('buildtype', get_option('buildtype'), section: 'Build Configuration')
summary('debug', get_option('debug'), section: 'Build Configuration')
summary('Bash completion', have_bash_completion, section: 'Shell integration')
summary('OpenGL', have_gl, section: 'Rendering APIs')
summary('GLES2', have_gles2, section: 'Rendering APIs')
summary('EGL', have_egl, section: 'Rendering APIs')

View File

@ -224,3 +224,9 @@ option('fonts',
value: true,
description: 'Enable font rendering integration using Pango'
)
option('bash_completion',
type: 'boolean',
value: true,
description: 'Integrate bash completion for gdctl'
)

View File

@ -1,11 +1,13 @@
#!/usr/bin/env python3
import argparse
import argcomplete
import sys
from dataclasses import dataclass
from gi.repository import GLib, Gio
from enum import Enum, Flag
from argcomplete.completers import BaseCompleter, SuppressCompleter
NAME = "org.gnome.Mutter.DisplayConfig"
INTERFACE = "org.gnome.Mutter.DisplayConfig"
@ -1130,6 +1132,70 @@ class GdctlParser(argparse.ArgumentParser):
return namespace
class MonitorCompleter(BaseCompleter):
def __call__(self, **kwargs):
try:
display_config = DisplayConfig()
monitors_state = MonitorsState(display_config)
return tuple(monitors_state.monitors)
except Exception:
return ()
class MonitorModeCompleter(BaseCompleter):
def __call__(self, parsed_args=None, **kwargs):
try:
(connector,) = parsed_args._current_sub_group["key"]
display_config = DisplayConfig()
monitors_state = MonitorsState(display_config)
monitor = monitors_state.monitors[connector]
return (mode.name for mode in monitor.modes)
except Exception:
return ()
class ScaleCompleter(BaseCompleter):
def __call__(self, parsed_args=None, **kwargs):
try:
(connector,) = parsed_args._current_sub_group["key"]
display_config = DisplayConfig()
monitors_state = MonitorsState(display_config)
monitor = monitors_state.monitors[connector]
mode = parsed_args._current_sub_group.get("mode", None)
if not mode:
mode = monitor.preferred_mode
scales = mode.supported_scales
scales.sort(key=lambda scale: abs(scale - mode.preferred_scale))
return (repr(scale) for scale in scales)
except Exception:
return ()
class NamedEnumCompleter(BaseCompleter):
def __init__(self, enum_type):
self.enum_type = enum_type
def __call__(self, **kwargs):
return (str(enum_value) for enum_value in self.enum_type)
class LayoutModeCompleter(NamedEnumCompleter):
def __init__(self):
super().__init__(LayoutMode)
class TransformCompleter(NamedEnumCompleter):
def __init__(self):
super().__init__(Transform)
if __name__ == "__main__":
parser = GdctlParser(
description="Display control utility",
@ -1196,7 +1262,7 @@ if __name__ == "__main__":
choices=[str(layout_mode) for layout_mode in list(LayoutMode)],
type=str,
action=AppendToGlobal,
)
).completer = LayoutModeCompleter()
set_parser.add_argument(
"-L",
"--logical-monitor",
@ -1216,8 +1282,9 @@ if __name__ == "__main__":
dest="monitors",
metavar="CONNECTOR",
action=SubGroupAction,
nargs=1,
help="Configure monitor",
)
).completer = MonitorCompleter()
monitor_parser = set_parser.add_argument_group(
"monitor",
"Monitor options (pass after --monitor)",
@ -1229,7 +1296,7 @@ if __name__ == "__main__":
action=AppendToSubGroup,
help="Monitor mode",
type=str,
)
).completer = MonitorModeCompleter()
logical_monitor_parser.add_argument(
"--primary",
"-p",
@ -1245,7 +1312,7 @@ if __name__ == "__main__":
action=AppendToGroup,
help="Logical monitor scale",
type=float,
)
).completer = ScaleCompleter()
logical_monitor_parser.add_argument(
"--transform",
"-t",
@ -1253,7 +1320,7 @@ if __name__ == "__main__":
help="Apply viewport transform",
choices=[str(transform) for transform in list(Transform)],
type=str,
)
).completer = TransformCompleter()
logical_monitor_parser.add_argument(
"--x",
"-x",
@ -1274,28 +1341,38 @@ if __name__ == "__main__":
metavar="CONNECTOR",
help="Place right of other monitor",
type=str,
)
).completer = MonitorCompleter()
logical_monitor_parser.add_argument(
"--left-of",
action=AppendToGroup,
metavar="CONNECTOR",
help="Place left of other monitor",
type=str,
)
).completer = MonitorCompleter()
logical_monitor_parser.add_argument(
"--above",
action=AppendToGroup,
metavar="CONNECTOR",
help="Place above other monitor",
type=str,
)
).completer = MonitorCompleter()
logical_monitor_parser.add_argument(
"--below",
action=AppendToGroup,
metavar="CONNECTOR",
help="Place below other monitor",
type=str,
)
).completer = MonitorCompleter()
for action in [
GroupAction,
SubGroupAction,
AppendToGroup,
AppendToSubGroup,
AppendToGlobal,
]:
argcomplete.safe_actions.add(action)
argcomplete.autocomplete(parser, default_completer=SuppressCompleter)
args = parser.parse_args()

View File

@ -3,5 +3,30 @@ install_data(
install_dir: bindir,
)
if have_bash_completion
bash_completion = dependency('bash-completion', required: false)
if bash_completion.found()
bash_completion_dir = bash_completion.get_variable(pkgconfig: 'completionsdir')
else
bash_completion_dir = get_option('sysconfdir') / 'bash_completion.d'
endif
register_python_argcomplete = find_program('register-python-argcomplete')
custom_target(
'gdctl-bash-completion',
output: 'gdctl',
command: [
register_python_argcomplete,
'gdctl',
'--complete-arguments',
'-o nosort',
],
capture: true,
install_dir: bash_completion_dir,
install: true,
)
endif
gdctl = find_program('gdctl')
get_state_tool = find_program('get-state.py')