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:
@ -11,6 +11,7 @@ include:
|
|||||||
meson-options:
|
meson-options:
|
||||||
-Dxwayland_initfd=enabled
|
-Dxwayland_initfd=enabled
|
||||||
-Dprofiler=true
|
-Dprofiler=true
|
||||||
|
-Dbash_completion=false
|
||||||
|
|
||||||
build-sysext:
|
build-sysext:
|
||||||
before_script:
|
before_script:
|
||||||
@ -124,6 +125,7 @@ variables:
|
|||||||
zenity
|
zenity
|
||||||
python3-dbusmock
|
python3-dbusmock
|
||||||
gnome-desktop-testing
|
gnome-desktop-testing
|
||||||
|
python3-argcomplete
|
||||||
ruff
|
ruff
|
||||||
|
|
||||||
FDO_DISTRIBUTION_EXEC: |
|
FDO_DISTRIBUTION_EXEC: |
|
||||||
@ -194,8 +196,6 @@ variables:
|
|||||||
mkdir -p /opt/mutter
|
mkdir -p /opt/mutter
|
||||||
cp build/src/tests/kvm/bzImage /opt/mutter/bzImage
|
cp build/src/tests/kvm/bzImage /opt/mutter/bzImage
|
||||||
|
|
||||||
dnf install -y python3-argcomplete
|
|
||||||
|
|
||||||
git clone https://github.com/arighi/virtme-ng.git
|
git clone https://github.com/arighi/virtme-ng.git
|
||||||
cd virtme-ng
|
cd virtme-ng
|
||||||
git fetch --tags
|
git fetch --tags
|
||||||
|
@ -378,6 +378,7 @@ have_kvm_tests = false
|
|||||||
have_tty_tests = false
|
have_tty_tests = false
|
||||||
have_installed_tests = false
|
have_installed_tests = false
|
||||||
have_x11_tests = false
|
have_x11_tests = false
|
||||||
|
have_bash_completion = get_option('bash_completion')
|
||||||
|
|
||||||
if have_tests
|
if have_tests
|
||||||
gtk3_dep = dependency('gtk+-3.0', version: gtk3_req)
|
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('buildtype', get_option('buildtype'), section: 'Build Configuration')
|
||||||
summary('debug', get_option('debug'), 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('OpenGL', have_gl, section: 'Rendering APIs')
|
||||||
summary('GLES2', have_gles2, section: 'Rendering APIs')
|
summary('GLES2', have_gles2, section: 'Rendering APIs')
|
||||||
summary('EGL', have_egl, section: 'Rendering APIs')
|
summary('EGL', have_egl, section: 'Rendering APIs')
|
||||||
|
@ -224,3 +224,9 @@ option('fonts',
|
|||||||
value: true,
|
value: true,
|
||||||
description: 'Enable font rendering integration using Pango'
|
description: 'Enable font rendering integration using Pango'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
option('bash_completion',
|
||||||
|
type: 'boolean',
|
||||||
|
value: true,
|
||||||
|
description: 'Integrate bash completion for gdctl'
|
||||||
|
)
|
||||||
|
95
tools/gdctl
95
tools/gdctl
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import argcomplete
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from gi.repository import GLib, Gio
|
from gi.repository import GLib, Gio
|
||||||
from enum import Enum, Flag
|
from enum import Enum, Flag
|
||||||
|
from argcomplete.completers import BaseCompleter, SuppressCompleter
|
||||||
|
|
||||||
NAME = "org.gnome.Mutter.DisplayConfig"
|
NAME = "org.gnome.Mutter.DisplayConfig"
|
||||||
INTERFACE = "org.gnome.Mutter.DisplayConfig"
|
INTERFACE = "org.gnome.Mutter.DisplayConfig"
|
||||||
@ -1130,6 +1132,70 @@ class GdctlParser(argparse.ArgumentParser):
|
|||||||
return namespace
|
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__":
|
if __name__ == "__main__":
|
||||||
parser = GdctlParser(
|
parser = GdctlParser(
|
||||||
description="Display control utility",
|
description="Display control utility",
|
||||||
@ -1196,7 +1262,7 @@ if __name__ == "__main__":
|
|||||||
choices=[str(layout_mode) for layout_mode in list(LayoutMode)],
|
choices=[str(layout_mode) for layout_mode in list(LayoutMode)],
|
||||||
type=str,
|
type=str,
|
||||||
action=AppendToGlobal,
|
action=AppendToGlobal,
|
||||||
)
|
).completer = LayoutModeCompleter()
|
||||||
set_parser.add_argument(
|
set_parser.add_argument(
|
||||||
"-L",
|
"-L",
|
||||||
"--logical-monitor",
|
"--logical-monitor",
|
||||||
@ -1216,8 +1282,9 @@ if __name__ == "__main__":
|
|||||||
dest="monitors",
|
dest="monitors",
|
||||||
metavar="CONNECTOR",
|
metavar="CONNECTOR",
|
||||||
action=SubGroupAction,
|
action=SubGroupAction,
|
||||||
|
nargs=1,
|
||||||
help="Configure monitor",
|
help="Configure monitor",
|
||||||
)
|
).completer = MonitorCompleter()
|
||||||
monitor_parser = set_parser.add_argument_group(
|
monitor_parser = set_parser.add_argument_group(
|
||||||
"monitor",
|
"monitor",
|
||||||
"Monitor options (pass after --monitor)",
|
"Monitor options (pass after --monitor)",
|
||||||
@ -1229,7 +1296,7 @@ if __name__ == "__main__":
|
|||||||
action=AppendToSubGroup,
|
action=AppendToSubGroup,
|
||||||
help="Monitor mode",
|
help="Monitor mode",
|
||||||
type=str,
|
type=str,
|
||||||
)
|
).completer = MonitorModeCompleter()
|
||||||
logical_monitor_parser.add_argument(
|
logical_monitor_parser.add_argument(
|
||||||
"--primary",
|
"--primary",
|
||||||
"-p",
|
"-p",
|
||||||
@ -1245,7 +1312,7 @@ if __name__ == "__main__":
|
|||||||
action=AppendToGroup,
|
action=AppendToGroup,
|
||||||
help="Logical monitor scale",
|
help="Logical monitor scale",
|
||||||
type=float,
|
type=float,
|
||||||
)
|
).completer = ScaleCompleter()
|
||||||
logical_monitor_parser.add_argument(
|
logical_monitor_parser.add_argument(
|
||||||
"--transform",
|
"--transform",
|
||||||
"-t",
|
"-t",
|
||||||
@ -1253,7 +1320,7 @@ if __name__ == "__main__":
|
|||||||
help="Apply viewport transform",
|
help="Apply viewport transform",
|
||||||
choices=[str(transform) for transform in list(Transform)],
|
choices=[str(transform) for transform in list(Transform)],
|
||||||
type=str,
|
type=str,
|
||||||
)
|
).completer = TransformCompleter()
|
||||||
logical_monitor_parser.add_argument(
|
logical_monitor_parser.add_argument(
|
||||||
"--x",
|
"--x",
|
||||||
"-x",
|
"-x",
|
||||||
@ -1274,28 +1341,38 @@ if __name__ == "__main__":
|
|||||||
metavar="CONNECTOR",
|
metavar="CONNECTOR",
|
||||||
help="Place right of other monitor",
|
help="Place right of other monitor",
|
||||||
type=str,
|
type=str,
|
||||||
)
|
).completer = MonitorCompleter()
|
||||||
logical_monitor_parser.add_argument(
|
logical_monitor_parser.add_argument(
|
||||||
"--left-of",
|
"--left-of",
|
||||||
action=AppendToGroup,
|
action=AppendToGroup,
|
||||||
metavar="CONNECTOR",
|
metavar="CONNECTOR",
|
||||||
help="Place left of other monitor",
|
help="Place left of other monitor",
|
||||||
type=str,
|
type=str,
|
||||||
)
|
).completer = MonitorCompleter()
|
||||||
logical_monitor_parser.add_argument(
|
logical_monitor_parser.add_argument(
|
||||||
"--above",
|
"--above",
|
||||||
action=AppendToGroup,
|
action=AppendToGroup,
|
||||||
metavar="CONNECTOR",
|
metavar="CONNECTOR",
|
||||||
help="Place above other monitor",
|
help="Place above other monitor",
|
||||||
type=str,
|
type=str,
|
||||||
)
|
).completer = MonitorCompleter()
|
||||||
logical_monitor_parser.add_argument(
|
logical_monitor_parser.add_argument(
|
||||||
"--below",
|
"--below",
|
||||||
action=AppendToGroup,
|
action=AppendToGroup,
|
||||||
metavar="CONNECTOR",
|
metavar="CONNECTOR",
|
||||||
help="Place below other monitor",
|
help="Place below other monitor",
|
||||||
type=str,
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -3,5 +3,30 @@ install_data(
|
|||||||
install_dir: bindir,
|
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')
|
gdctl = find_program('gdctl')
|
||||||
get_state_tool = find_program('get-state.py')
|
get_state_tool = find_program('get-state.py')
|
||||||
|
Reference in New Issue
Block a user