terminator-dbus 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- terminator_dbus/__init__.py +29 -0
- terminator_dbus/__main__.py +32 -0
- terminator_dbus/_internal/__init__.py +17 -0
- terminator_dbus/_internal/cli.py +74 -0
- terminator_dbus/_internal/client.py +294 -0
- terminator_dbus/_internal/debug.py +125 -0
- terminator_dbus/py.typed +0 -0
- terminator_dbus-0.1.0.dist-info/METADATA +159 -0
- terminator_dbus-0.1.0.dist-info/RECORD +12 -0
- terminator_dbus-0.1.0.dist-info/WHEEL +4 -0
- terminator_dbus-0.1.0.dist-info/entry_points.txt +5 -0
- terminator_dbus-0.1.0.dist-info/licenses/LICENSE +15 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2026, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
"""Terminator D-Bus package.
|
|
20
|
+
|
|
21
|
+
Python library to interact with Terminator through D-Bus.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from terminator_dbus._internal.cli import get_parser, main
|
|
27
|
+
from terminator_dbus._internal.client import BUS_BASE, BUS_PATH, Terminator, get_bus_name
|
|
28
|
+
|
|
29
|
+
__all__: list[str] = ["BUS_BASE", "BUS_PATH", "Terminator", "get_bus_name", "get_parser", "main"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2026, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
"""Entry-point module, in case you use `python -m terminator_dbus`.
|
|
20
|
+
|
|
21
|
+
Why does this file exist, and why `__main__`? For more info, read:
|
|
22
|
+
|
|
23
|
+
- https://www.python.org/dev/peps/pep-0338/
|
|
24
|
+
- https://docs.python.org/3/using/cmdline.html#cmdoption-m
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
from terminator_dbus._internal.cli import main
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2026, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2026, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
# Why does this file exist, and why not put this in `__main__`?
|
|
20
|
+
#
|
|
21
|
+
# You might be tempted to import things from `__main__` later,
|
|
22
|
+
# but that will cause problems: the code will get executed twice:
|
|
23
|
+
#
|
|
24
|
+
# - When you run `python -m terminator_dbus` python will execute
|
|
25
|
+
# `__main__.py` as a script. That means there won't be any
|
|
26
|
+
# `terminator_dbus.__main__` in `sys.modules`.
|
|
27
|
+
# - When you import `__main__` it will get executed again (as a module) because
|
|
28
|
+
# there's no `terminator_dbus.__main__` in `sys.modules`.
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import argparse
|
|
33
|
+
import sys
|
|
34
|
+
from typing import Any
|
|
35
|
+
|
|
36
|
+
from terminator_dbus._internal import debug
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class _DebugInfo(argparse.Action):
|
|
40
|
+
def __init__(self, nargs: int | str | None = 0, **kwargs: Any) -> None:
|
|
41
|
+
super().__init__(nargs=nargs, **kwargs)
|
|
42
|
+
|
|
43
|
+
def __call__(self, *args: Any, **kwargs: Any) -> None: # noqa: ARG002
|
|
44
|
+
debug._print_debug_info()
|
|
45
|
+
sys.exit(0)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_parser() -> argparse.ArgumentParser:
|
|
49
|
+
"""Return the CLI argument parser.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
An argparse parser.
|
|
53
|
+
"""
|
|
54
|
+
parser = argparse.ArgumentParser(prog="terminator-dbus")
|
|
55
|
+
parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {debug._get_version()}")
|
|
56
|
+
parser.add_argument("--debug-info", action=_DebugInfo, help="Print debug information.")
|
|
57
|
+
return parser
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main(args: list[str] | None = None) -> int:
|
|
61
|
+
"""Run the main program.
|
|
62
|
+
|
|
63
|
+
This function is executed when you type `terminator-dbus` or `python -m terminator_dbus`.
|
|
64
|
+
|
|
65
|
+
Parameters:
|
|
66
|
+
args: Arguments passed from the command line.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
An exit code.
|
|
70
|
+
"""
|
|
71
|
+
parser = get_parser()
|
|
72
|
+
opts = parser.parse_args(args=args)
|
|
73
|
+
print(opts)
|
|
74
|
+
return 0
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2026, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import hashlib
|
|
22
|
+
import os
|
|
23
|
+
from typing import TYPE_CHECKING, Any, Final
|
|
24
|
+
|
|
25
|
+
import dbus
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from collections.abc import Mapping
|
|
29
|
+
|
|
30
|
+
from dbus.bus import BusConnection
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
BUS_BASE: Final = "net.tenshu.Terminator2"
|
|
34
|
+
"""Base name of Terminator's D-Bus service and interface."""
|
|
35
|
+
|
|
36
|
+
BUS_PATH: Final = "/net/tenshu/Terminator2"
|
|
37
|
+
"""Object path of Terminator's D-Bus service."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _default_display() -> str | None:
|
|
41
|
+
backend = os.environ.get("GDK_BACKEND", "").split(",", maxsplit=1)[0]
|
|
42
|
+
if backend == "wayland":
|
|
43
|
+
return os.environ.get("WAYLAND_DISPLAY") or os.environ.get("DISPLAY")
|
|
44
|
+
if backend == "x11":
|
|
45
|
+
return os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")
|
|
46
|
+
if os.environ.get("XDG_SESSION_TYPE") == "wayland":
|
|
47
|
+
return os.environ.get("WAYLAND_DISPLAY") or os.environ.get("DISPLAY")
|
|
48
|
+
return os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_bus_name(display: str | None = None) -> str:
|
|
52
|
+
"""Return the D-Bus service and interface name for a display.
|
|
53
|
+
|
|
54
|
+
Terminator appends the MD5 digest of its GDK display name to
|
|
55
|
+
`net.tenshu.Terminator2`. It removes the screen suffix before it creates
|
|
56
|
+
the digest. For example, `:0` and `:0.0` use the same D-Bus name.
|
|
57
|
+
|
|
58
|
+
Parameters:
|
|
59
|
+
display: GDK display name. The current desktop display is used when
|
|
60
|
+
this argument is not set.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
The display-specific name, or the base name when no display is known.
|
|
64
|
+
"""
|
|
65
|
+
if display is None:
|
|
66
|
+
display = _default_display()
|
|
67
|
+
if not display:
|
|
68
|
+
return BUS_BASE
|
|
69
|
+
|
|
70
|
+
display_without_screen = display.partition(".")[0]
|
|
71
|
+
display_digest = hashlib.md5(display_without_screen.encode(), usedforsecurity=False).hexdigest()
|
|
72
|
+
return f"{BUS_BASE}{display_digest}"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _options(values: Mapping[str, str] | None = None) -> dbus.Dictionary:
|
|
76
|
+
return dbus.Dictionary({} if values is None else values, signature="ss")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _optional_string(value: object) -> str | None:
|
|
80
|
+
if value is None:
|
|
81
|
+
return None
|
|
82
|
+
return str(value)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class Terminator:
|
|
86
|
+
"""Provide Python methods for Terminator's complete D-Bus interface.
|
|
87
|
+
|
|
88
|
+
The client keeps one session-bus connection and one proxy. It sends known
|
|
89
|
+
method signatures directly, so `dbus-python` does not need an
|
|
90
|
+
introspection request before the first method call.
|
|
91
|
+
|
|
92
|
+
Parameters:
|
|
93
|
+
bus: An existing D-Bus connection. A session-bus connection is created
|
|
94
|
+
when this argument is not set.
|
|
95
|
+
display: GDK display name used to calculate Terminator's D-Bus name.
|
|
96
|
+
bus_name: Exact D-Bus service name. Use this argument for a custom or
|
|
97
|
+
already discovered Terminator service.
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
ValueError: Both `display` and `bus_name` were specified.
|
|
101
|
+
dbus.DBusException: The session bus or Terminator service is not
|
|
102
|
+
available.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(
|
|
106
|
+
self,
|
|
107
|
+
*,
|
|
108
|
+
bus: BusConnection | None = None,
|
|
109
|
+
display: str | None = None,
|
|
110
|
+
bus_name: str | None = None,
|
|
111
|
+
) -> None:
|
|
112
|
+
if display is not None and bus_name is not None:
|
|
113
|
+
raise ValueError("display and bus_name cannot both be specified")
|
|
114
|
+
|
|
115
|
+
self._bus_name = get_bus_name(display) if bus_name is None else bus_name
|
|
116
|
+
self._bus = dbus.SessionBus() if bus is None else bus
|
|
117
|
+
proxy = self._bus.get_object(self.bus_name, BUS_PATH, introspect=False)
|
|
118
|
+
self._interface = dbus.Interface(proxy, self.bus_name)
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def bus_name(self) -> str:
|
|
122
|
+
"""Return the D-Bus service and interface name used by this client."""
|
|
123
|
+
return self._bus_name
|
|
124
|
+
|
|
125
|
+
def _call(self, method_name: str, *args: object, signature: str) -> Any:
|
|
126
|
+
method = self._interface.get_dbus_method(method_name)
|
|
127
|
+
return method(*args, signature=signature)
|
|
128
|
+
|
|
129
|
+
def new_window_cmdline(self, options: Mapping[str, str]) -> None:
|
|
130
|
+
"""Create a window from serialized Terminator command-line options.
|
|
131
|
+
|
|
132
|
+
Parameters:
|
|
133
|
+
options: Complete option mapping produced by Terminator's command
|
|
134
|
+
line parser. Keys and values must be strings.
|
|
135
|
+
"""
|
|
136
|
+
self._call("new_window_cmdline", _options(options), signature="a{ss}")
|
|
137
|
+
|
|
138
|
+
def new_tab_cmdline(self, options: Mapping[str, str]) -> None:
|
|
139
|
+
"""Create a tab from serialized Terminator command-line options.
|
|
140
|
+
|
|
141
|
+
Parameters:
|
|
142
|
+
options: Complete option mapping produced by Terminator's command
|
|
143
|
+
line parser. Keys and values must be strings.
|
|
144
|
+
"""
|
|
145
|
+
self._call("new_tab_cmdline", _options(options), signature="a{ss}")
|
|
146
|
+
|
|
147
|
+
def toggle_visibility_cmdline(self, options: Mapping[str, str]) -> None:
|
|
148
|
+
"""Toggle the visibility of every Terminator window.
|
|
149
|
+
|
|
150
|
+
Parameters:
|
|
151
|
+
options: Serialized command-line options. Terminator currently
|
|
152
|
+
ignores the values.
|
|
153
|
+
"""
|
|
154
|
+
self._call("toggle_visibility_cmdline", _options(options), signature="a{ss}")
|
|
155
|
+
|
|
156
|
+
def unhide_cmdline(self, options: Mapping[str, str]) -> None:
|
|
157
|
+
"""Show every hidden Terminator window.
|
|
158
|
+
|
|
159
|
+
Parameters:
|
|
160
|
+
options: Serialized command-line options. Terminator currently
|
|
161
|
+
ignores the values.
|
|
162
|
+
"""
|
|
163
|
+
self._call("unhide_cmdline", _options(options), signature="a{ss}")
|
|
164
|
+
|
|
165
|
+
def new_window(self) -> str:
|
|
166
|
+
"""Create a window and return its first terminal UUID."""
|
|
167
|
+
return str(self._call("new_window", signature=""))
|
|
168
|
+
|
|
169
|
+
def new_tab(self, uuid: str) -> str:
|
|
170
|
+
"""Create a tab beside a terminal and return the new terminal UUID.
|
|
171
|
+
|
|
172
|
+
Parameters:
|
|
173
|
+
uuid: UUID of a terminal in the target window.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
The new terminal UUID or an error string from Terminator.
|
|
177
|
+
"""
|
|
178
|
+
return str(self._call("new_tab", uuid, signature="v"))
|
|
179
|
+
|
|
180
|
+
def reload_configuration(self) -> None:
|
|
181
|
+
"""Reload the configuration of all terminals."""
|
|
182
|
+
self._call("reload_configuration", signature="")
|
|
183
|
+
|
|
184
|
+
def bg_img_all(self, options: Mapping[str, str]) -> None:
|
|
185
|
+
"""Set the background image of all terminals.
|
|
186
|
+
|
|
187
|
+
Parameters:
|
|
188
|
+
options: Mapping with the image path in the `file` key.
|
|
189
|
+
"""
|
|
190
|
+
self._call("bg_img_all", _options(options), signature="v")
|
|
191
|
+
|
|
192
|
+
def bg_img(self, uuid: str, options: Mapping[str, str]) -> None:
|
|
193
|
+
"""Set the background image of one terminal.
|
|
194
|
+
|
|
195
|
+
Parameters:
|
|
196
|
+
uuid: Target terminal UUID.
|
|
197
|
+
options: Mapping with the image path in the `file` key.
|
|
198
|
+
"""
|
|
199
|
+
self._call("bg_img", uuid, _options(options), signature="vv")
|
|
200
|
+
|
|
201
|
+
def hsplit(self, uuid: str, options: Mapping[str, str] | None = None) -> str:
|
|
202
|
+
"""Split a terminal horizontally and return the new terminal UUID.
|
|
203
|
+
|
|
204
|
+
Parameters:
|
|
205
|
+
uuid: Target terminal UUID.
|
|
206
|
+
options: Optional `execute` command and `title` for the new
|
|
207
|
+
terminal.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
The new terminal UUID or an error string from Terminator.
|
|
211
|
+
"""
|
|
212
|
+
return str(self._call("hsplit", uuid, _options(options), signature="vv"))
|
|
213
|
+
|
|
214
|
+
def vsplit(self, uuid: str, options: Mapping[str, str] | None = None) -> str:
|
|
215
|
+
"""Split a terminal vertically and return the new terminal UUID.
|
|
216
|
+
|
|
217
|
+
Parameters:
|
|
218
|
+
uuid: Target terminal UUID.
|
|
219
|
+
options: Optional `execute` command and `title` for the new
|
|
220
|
+
terminal.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
The new terminal UUID or an error string from Terminator.
|
|
224
|
+
"""
|
|
225
|
+
return str(self._call("vsplit", uuid, _options(options), signature="vv"))
|
|
226
|
+
|
|
227
|
+
def get_terminals(self) -> list[str]:
|
|
228
|
+
"""Return the UUID of every terminal."""
|
|
229
|
+
return [str(uuid) for uuid in self._call("get_terminals", signature="")]
|
|
230
|
+
|
|
231
|
+
def get_focused_terminal(self) -> str | None:
|
|
232
|
+
"""Return the focused terminal UUID, if a terminal has focus."""
|
|
233
|
+
return _optional_string(self._call("get_focused_terminal", signature=""))
|
|
234
|
+
|
|
235
|
+
def get_window(self, uuid: str) -> str:
|
|
236
|
+
"""Return the parent window UUID of a terminal.
|
|
237
|
+
|
|
238
|
+
Parameters:
|
|
239
|
+
uuid: Target terminal UUID.
|
|
240
|
+
"""
|
|
241
|
+
return str(self._call("get_window", uuid, signature="v"))
|
|
242
|
+
|
|
243
|
+
def get_window_title(self, uuid: str) -> str:
|
|
244
|
+
"""Return the parent window title of a terminal.
|
|
245
|
+
|
|
246
|
+
Parameters:
|
|
247
|
+
uuid: Target terminal UUID.
|
|
248
|
+
"""
|
|
249
|
+
return str(self._call("get_window_title", uuid, signature="v"))
|
|
250
|
+
|
|
251
|
+
def get_tab(self, uuid: str) -> str | None:
|
|
252
|
+
"""Return the parent tab identifier of a terminal.
|
|
253
|
+
|
|
254
|
+
Terminator does not assign UUIDs to tabs. It currently returns an
|
|
255
|
+
empty string for a terminal in a notebook and no value otherwise.
|
|
256
|
+
|
|
257
|
+
Parameters:
|
|
258
|
+
uuid: Target terminal UUID.
|
|
259
|
+
"""
|
|
260
|
+
return _optional_string(self._call("get_tab", uuid, signature="v"))
|
|
261
|
+
|
|
262
|
+
def get_tab_title(self, uuid: str) -> str | None:
|
|
263
|
+
"""Return the parent tab title of a terminal, if it has one.
|
|
264
|
+
|
|
265
|
+
Parameters:
|
|
266
|
+
uuid: Target terminal UUID.
|
|
267
|
+
"""
|
|
268
|
+
return _optional_string(self._call("get_tab_title", uuid, signature="v"))
|
|
269
|
+
|
|
270
|
+
def set_tab_title(self, uuid: str, options: Mapping[str, str]) -> None:
|
|
271
|
+
"""Set a terminal's parent tab title.
|
|
272
|
+
|
|
273
|
+
Parameters:
|
|
274
|
+
uuid: Target terminal UUID.
|
|
275
|
+
options: Mapping with the new title in the `tab-title` key.
|
|
276
|
+
"""
|
|
277
|
+
self._call("set_tab_title", uuid, _options(options), signature="vv")
|
|
278
|
+
|
|
279
|
+
def switch_profile(self, uuid: str, options: Mapping[str, str]) -> None:
|
|
280
|
+
"""Switch one terminal to a profile.
|
|
281
|
+
|
|
282
|
+
Parameters:
|
|
283
|
+
uuid: Target terminal UUID.
|
|
284
|
+
options: Mapping with the profile name in the `profile` key.
|
|
285
|
+
"""
|
|
286
|
+
self._call("switch_profile", uuid, _options(options), signature="vv")
|
|
287
|
+
|
|
288
|
+
def switch_profile_all(self, options: Mapping[str, str]) -> None:
|
|
289
|
+
"""Switch all terminals to a profile.
|
|
290
|
+
|
|
291
|
+
Parameters:
|
|
292
|
+
options: Mapping with the profile name in the `profile` key.
|
|
293
|
+
"""
|
|
294
|
+
self._call("switch_profile_all", _options(options), signature="v")
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# SPDX-License-Identifier: ISC
|
|
2
|
+
#
|
|
3
|
+
# ISC License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2026, Timothée Mazzucotelli and contributors
|
|
6
|
+
#
|
|
7
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
9
|
+
# copyright notice and this permission notice appear in all copies.
|
|
10
|
+
#
|
|
11
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
12
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
14
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
16
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
17
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import platform
|
|
23
|
+
import sys
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from importlib import metadata
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class _Variable:
|
|
30
|
+
"""Dataclass describing an environment variable."""
|
|
31
|
+
|
|
32
|
+
name: str
|
|
33
|
+
"""Variable name."""
|
|
34
|
+
value: str
|
|
35
|
+
"""Variable value."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class _Package:
|
|
40
|
+
"""Dataclass describing a Python package."""
|
|
41
|
+
|
|
42
|
+
name: str
|
|
43
|
+
"""Package name."""
|
|
44
|
+
version: str
|
|
45
|
+
"""Package version."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class _Environment:
|
|
50
|
+
"""Dataclass to store environment information."""
|
|
51
|
+
|
|
52
|
+
interpreter_name: str
|
|
53
|
+
"""Python interpreter name."""
|
|
54
|
+
interpreter_version: str
|
|
55
|
+
"""Python interpreter version."""
|
|
56
|
+
interpreter_path: str
|
|
57
|
+
"""Path to Python executable."""
|
|
58
|
+
platform: str
|
|
59
|
+
"""Operating System."""
|
|
60
|
+
packages: list[_Package]
|
|
61
|
+
"""Installed packages."""
|
|
62
|
+
variables: list[_Variable]
|
|
63
|
+
"""Environment variables."""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _interpreter_name_version() -> tuple[str, str]:
|
|
67
|
+
if hasattr(sys, "implementation"):
|
|
68
|
+
impl = sys.implementation.version
|
|
69
|
+
version = f"{impl.major}.{impl.minor}.{impl.micro}"
|
|
70
|
+
kind = impl.releaselevel
|
|
71
|
+
if kind != "final":
|
|
72
|
+
version += kind[0] + str(impl.serial)
|
|
73
|
+
return sys.implementation.name, version
|
|
74
|
+
return "", "0.0.0"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _get_version(dist: str = "terminator-dbus") -> str:
|
|
78
|
+
"""Get version of the given distribution.
|
|
79
|
+
|
|
80
|
+
Parameters:
|
|
81
|
+
dist: A distribution name.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
A version number.
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
return metadata.version(dist)
|
|
88
|
+
except metadata.PackageNotFoundError:
|
|
89
|
+
return "0.0.0"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _get_debug_info() -> _Environment:
|
|
93
|
+
"""Get debug/environment information.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Environment information.
|
|
97
|
+
"""
|
|
98
|
+
py_name, py_version = _interpreter_name_version()
|
|
99
|
+
packages = ["terminator-dbus"]
|
|
100
|
+
variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("TERMINATOR_DBUS")]]
|
|
101
|
+
return _Environment(
|
|
102
|
+
interpreter_name=py_name,
|
|
103
|
+
interpreter_version=py_version,
|
|
104
|
+
interpreter_path=sys.executable,
|
|
105
|
+
platform=platform.platform(),
|
|
106
|
+
variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))],
|
|
107
|
+
packages=[_Package(pkg, _get_version(pkg)) for pkg in packages],
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _print_debug_info() -> None:
|
|
112
|
+
"""Print debug/environment information."""
|
|
113
|
+
info = _get_debug_info()
|
|
114
|
+
print(f"- __System__: {info.platform}")
|
|
115
|
+
print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})")
|
|
116
|
+
print("- __Environment variables__:")
|
|
117
|
+
for var in info.variables:
|
|
118
|
+
print(f" - `{var.name}`: `{var.value}`")
|
|
119
|
+
print("- __Installed packages__:")
|
|
120
|
+
for pkg in info.packages:
|
|
121
|
+
print(f" - `{pkg.name}` v{pkg.version}")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == "__main__":
|
|
125
|
+
_print_debug_info()
|
terminator_dbus/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: terminator-dbus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python library to interact with Terminator through D-Bus.
|
|
5
|
+
Author-Email: =?utf-8?q?Timoth=C3=A9e_Mazzucotelli?= <dev@pawamoy.fr>
|
|
6
|
+
License-Expression: ISC
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Documentation
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Project-URL: Homepage, https://pawamoy.github.io/terminator-dbus
|
|
23
|
+
Project-URL: Documentation, https://pawamoy.github.io/terminator-dbus
|
|
24
|
+
Project-URL: Changelog, https://pawamoy.github.io/terminator-dbus/changelog
|
|
25
|
+
Project-URL: Repository, https://github.com/pawamoy/terminator-dbus
|
|
26
|
+
Project-URL: Issues, https://github.com/pawamoy/terminator-dbus/issues
|
|
27
|
+
Project-URL: Discussions, https://github.com/pawamoy/terminator-dbus/discussions
|
|
28
|
+
Project-URL: Gitter, https://gitter.im/terminator-dbus/community
|
|
29
|
+
Project-URL: Funding, https://github.com/sponsors/pawamoy
|
|
30
|
+
Requires-Python: >=3.10
|
|
31
|
+
Requires-Dist: dbus-python>=1.3.2
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# Terminator D-Bus
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
[](https://github.com/pawamoy/terminator-dbus/actions?query=workflow%3Aci)
|
|
38
|
+
[](https://pawamoy.github.io/terminator-dbus/)
|
|
39
|
+
[](https://pypi.org/project/terminator-dbus/)
|
|
40
|
+
[](https://app.gitter.im/#/room/#terminator-dbus:gitter.im)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Python library to interact with Terminator through D-Bus.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install terminator-dbus
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
With [`uv`](https://docs.astral.sh/uv/):
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uv tool install terminator-dbus
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
`dbus-python` needs the D-Bus development files when no system package or wheel is available.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
Create one client and reuse it for all calls:
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from terminator_dbus import Terminator
|
|
73
|
+
|
|
74
|
+
terminator = Terminator()
|
|
75
|
+
terminal = terminator.get_focused_terminal()
|
|
76
|
+
|
|
77
|
+
if terminal is not None:
|
|
78
|
+
new_terminal = terminator.vsplit(
|
|
79
|
+
terminal,
|
|
80
|
+
{"execute": "htop", "title": "Processes"},
|
|
81
|
+
)
|
|
82
|
+
terminator.switch_profile(new_terminal, {"profile": "work"})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
The client uses the current X11 or Wayland display by default. Pass `display` if the current environment does not identify the correct display:
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
terminator = Terminator(display=":1")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
Pass `bus_name` when you already know the complete D-Bus service name:
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
terminator = Terminator(bus_name="net.tenshu.Terminator2...")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
`dbus.DBusException` reports connection and remote method failures. Terminator itself returns strings that start with `ERROR:` for some tab and split failures.
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
## D-Bus interface
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
This package follows the interface in [Terminator 2.1.6 `ipc.py`](https://github.com/gnome-terminator/terminator/blob/v2.1.6/terminatorlib/ipc.py).
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
- Base service and interface name: `net.tenshu.Terminator2`
|
|
112
|
+
- Object path: `/net/tenshu/Terminator2`
|
|
113
|
+
- Display-specific name: the base name followed by the MD5 digest of the GDK display name
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
Terminator removes the screen suffix before it creates the digest. Thus, `:0` and `:0.0` use the same service name.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
The `Terminator` class exposes every method in the service:
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
| Python method | D-Bus input | Purpose | Result |
|
|
123
|
+
| --- | --- | --- | --- |
|
|
124
|
+
| `new_window_cmdline(options)` | `a{ss}` | Create a window from serialized command-line options. | `None` |
|
|
125
|
+
| `new_tab_cmdline(options)` | `a{ss}` | Create a tab from serialized command-line options. | `None` |
|
|
126
|
+
| `toggle_visibility_cmdline(options)` | `a{ss}` | Toggle all window visibility. | `None` |
|
|
127
|
+
| `unhide_cmdline(options)` | `a{ss}` | Show all hidden windows. | `None` |
|
|
128
|
+
| `new_window()` | empty | Create a window. | New terminal UUID or error string |
|
|
129
|
+
| `new_tab(uuid)` | `v` | Create a tab in a terminal's window. | New terminal UUID or error string |
|
|
130
|
+
| `reload_configuration()` | empty | Reload configuration for all terminals. | `None` |
|
|
131
|
+
| `bg_img_all(options)` | `v` | Set all background images from the `file` option. | `None` |
|
|
132
|
+
| `bg_img(uuid, options)` | `vv` | Set one background image from the `file` option. | `None` |
|
|
133
|
+
| `hsplit(uuid, options=None)` | `vv` | Split a terminal horizontally. | New terminal UUID or error string |
|
|
134
|
+
| `vsplit(uuid, options=None)` | `vv` | Split a terminal vertically. | New terminal UUID or error string |
|
|
135
|
+
| `get_terminals()` | empty | Get all terminal UUIDs. | `list[str]` |
|
|
136
|
+
| `get_focused_terminal()` | empty | Get the focused terminal UUID. | `str \| None` |
|
|
137
|
+
| `get_window(uuid)` | `v` | Get a terminal's window UUID. | `str` |
|
|
138
|
+
| `get_window_title(uuid)` | `v` | Get a terminal's window title. | `str` |
|
|
139
|
+
| `get_tab(uuid)` | `v` | Get a terminal's tab identifier. | `str \| None` |
|
|
140
|
+
| `get_tab_title(uuid)` | `v` | Get a terminal's tab title. | `str \| None` |
|
|
141
|
+
| `set_tab_title(uuid, options)` | `vv` | Set a tab title from the `tab-title` option. | `None` |
|
|
142
|
+
| `switch_profile(uuid, options)` | `vv` | Set one profile from the `profile` option. | `None` |
|
|
143
|
+
| `switch_profile_all(options)` | `v` | Set all profiles from the `profile` option. | `None` |
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
Terminator does not assign identifiers to tabs. Its `get_tab` method returns an empty string for a terminal in a notebook.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
The four `*_cmdline` methods require string-to-string dictionaries. The window and tab creation methods require the complete option mapping from Terminator's command-line parser.
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
Terminator does not declare D-Bus output signatures. `dbus-python` infers each output signature from the value returned by the service method.
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
## Sponsors
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
<!-- sponsors-start -->
|
|
159
|
+
<!-- sponsors-end -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
terminator_dbus-0.1.0.dist-info/METADATA,sha256=ZIBNR-ltKtm_viLdTwCF40e7AUcgijgMMFdEpfFFbW8,6207
|
|
2
|
+
terminator_dbus-0.1.0.dist-info/WHEEL,sha256=Y8xFLOH-FrS4gLE6ON4ti06sl2W_9QUDiRaTK3tfwig,91
|
|
3
|
+
terminator_dbus-0.1.0.dist-info/entry_points.txt,sha256=DZGbTVz8MoUuz56ejS6uA5L40r5cNGLW3XVgifiQx8A,73
|
|
4
|
+
terminator_dbus-0.1.0.dist-info/licenses/LICENSE,sha256=I3KB1-ErGw-w8Lv5cPRgpAQKNKPv_CpOJvpYunP93-E,771
|
|
5
|
+
terminator_dbus/__init__.py,sha256=2__lOnprDspKWl2Wmx0MCBjZDNFoDO8BpM6rJ69dZGM,1209
|
|
6
|
+
terminator_dbus/__main__.py,sha256=QXkwKNVavUPbXObqIR3r29pvZ3xDdDMGeeJTFej11cQ,1197
|
|
7
|
+
terminator_dbus/_internal/__init__.py,sha256=LH7HQJydqOYIxZva6Nc0p5zeUpQAxcvUyEwiajkZkdQ,831
|
|
8
|
+
terminator_dbus/_internal/cli.py,sha256=4vW6wRTVncUcHsTsjErEAmFi-FcjytOmGhDO8DnWjBA,2563
|
|
9
|
+
terminator_dbus/_internal/client.py,sha256=SgBYyGQnXoR64Yws_Etv4wbtTbnHLtrgM7N9FLQYb-c,11016
|
|
10
|
+
terminator_dbus/_internal/debug.py,sha256=xrgL0cQWdtOrxh8lFYhwQPTRW-Wfe0Y-YOzE4-d3YZE,3665
|
|
11
|
+
terminator_dbus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
terminator_dbus-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Timothée Mazzucotelli and contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|