python-libei 0.6.1__py3-none-any.whl → 0.7.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.
- libei/__init__.py +1 -1
- libei/portal.py +57 -1
- {python_libei-0.6.1.dist-info → python_libei-0.7.0.dist-info}/METADATA +3 -2
- {python_libei-0.6.1.dist-info → python_libei-0.7.0.dist-info}/RECORD +7 -7
- {python_libei-0.6.1.dist-info → python_libei-0.7.0.dist-info}/WHEEL +0 -0
- {python_libei-0.6.1.dist-info → python_libei-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {python_libei-0.6.1.dist-info → python_libei-0.7.0.dist-info}/top_level.txt +0 -0
libei/__init__.py
CHANGED
libei/portal.py
CHANGED
|
@@ -185,6 +185,28 @@ class PortalDeniedError(PortalError):
|
|
|
185
185
|
self.message = message
|
|
186
186
|
|
|
187
187
|
|
|
188
|
+
class PortalSessionClosedError(PortalError):
|
|
189
|
+
"""The portal session ended while something was still waiting on it.
|
|
190
|
+
|
|
191
|
+
The ``org.freedesktop.portal.Session`` interface emits ``Closed`` when
|
|
192
|
+
the compositor or the portal ends a session from its side -- a
|
|
193
|
+
compositor restart, the portal process dying, a session revoked from a
|
|
194
|
+
settings panel. Distinct from :class:`PortalTimeoutError` (the session
|
|
195
|
+
is fine, the signal just has not come) and from
|
|
196
|
+
:class:`PortalDeniedError` (a request was answered, negatively): here
|
|
197
|
+
there is no session left to answer anything, so the wait that raised
|
|
198
|
+
this can never be satisfied by waiting longer.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
def __init__(self, step: str) -> None:
|
|
202
|
+
"""Name what was being waited for when the session went away."""
|
|
203
|
+
super().__init__(
|
|
204
|
+
f"the portal session was closed while waiting for {step}; "
|
|
205
|
+
"the compositor or portal ended it from its side"
|
|
206
|
+
)
|
|
207
|
+
self.step = step
|
|
208
|
+
|
|
209
|
+
|
|
188
210
|
def _gio() -> tuple[Any, Any] | None:
|
|
189
211
|
"""Import Gio and GLib, or return None.
|
|
190
212
|
|
|
@@ -1038,6 +1060,27 @@ def _wait_for_signal(
|
|
|
1038
1060
|
loop = GLib.MainLoop()
|
|
1039
1061
|
result: dict[str, Any] = {}
|
|
1040
1062
|
timed_out = False
|
|
1063
|
+
session_closed = False
|
|
1064
|
+
|
|
1065
|
+
def on_closed(*_args: Any) -> None:
|
|
1066
|
+
"""End the wait: a closed session can never deliver the signal.
|
|
1067
|
+
|
|
1068
|
+
``Closed`` is emitted on the **session object's own path** (unlike
|
|
1069
|
+
``Activated``/``Deactivated``, which fire on the portal object --
|
|
1070
|
+
see above), so the subscription below uses ``session_handle`` as
|
|
1071
|
+
the path and needs no payload filtering: any ``Closed`` there is
|
|
1072
|
+
this session's. Without this subscription, a wait with
|
|
1073
|
+
``timeout=None`` on a session the compositor has ended -- a
|
|
1074
|
+
compositor restart, the portal dying, capture revoked from a
|
|
1075
|
+
settings panel -- blocked forever, indistinguishable from a signal
|
|
1076
|
+
that merely had not fired yet.
|
|
1077
|
+
"""
|
|
1078
|
+
nonlocal session_closed
|
|
1079
|
+
if result:
|
|
1080
|
+
return
|
|
1081
|
+
logger.debug("session %s closed while waiting for %s", session_handle, signal)
|
|
1082
|
+
session_closed = True
|
|
1083
|
+
loop.quit()
|
|
1041
1084
|
|
|
1042
1085
|
def on_signal(
|
|
1043
1086
|
_conn: Any,
|
|
@@ -1086,6 +1129,16 @@ def _wait_for_signal(
|
|
|
1086
1129
|
on_signal,
|
|
1087
1130
|
None,
|
|
1088
1131
|
)
|
|
1132
|
+
closed_subscription = connection.signal_subscribe(
|
|
1133
|
+
busname,
|
|
1134
|
+
_SESSION_INTERFACE,
|
|
1135
|
+
"Closed",
|
|
1136
|
+
session_handle,
|
|
1137
|
+
None,
|
|
1138
|
+
Gio.DBusSignalFlags.NONE,
|
|
1139
|
+
on_closed,
|
|
1140
|
+
None,
|
|
1141
|
+
)
|
|
1089
1142
|
logger.debug(
|
|
1090
1143
|
"waiting up to %s for %s.%s on %s for session %s",
|
|
1091
1144
|
"forever" if timeout is None else f"{timeout:g}s",
|
|
@@ -1095,7 +1148,7 @@ def _wait_for_signal(
|
|
|
1095
1148
|
session_handle,
|
|
1096
1149
|
)
|
|
1097
1150
|
try:
|
|
1098
|
-
if not result:
|
|
1151
|
+
if not result and not session_closed:
|
|
1099
1152
|
timeout_source = None
|
|
1100
1153
|
if timeout is not None:
|
|
1101
1154
|
timeout_source = GLib.timeout_add(int(timeout * 1000), on_timeout)
|
|
@@ -1115,6 +1168,9 @@ def _wait_for_signal(
|
|
|
1115
1168
|
GLib.source_remove(timeout_source)
|
|
1116
1169
|
finally:
|
|
1117
1170
|
connection.signal_unsubscribe(subscription)
|
|
1171
|
+
connection.signal_unsubscribe(closed_subscription)
|
|
1172
|
+
if session_closed:
|
|
1173
|
+
raise PortalSessionClosedError(signal)
|
|
1118
1174
|
if timed_out:
|
|
1119
1175
|
# timed_out is only ever set inside on_timeout, itself only ever
|
|
1120
1176
|
# registered when timeout is not None -- so this always holds, but
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-libei
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Inject, receive and capture input on Wayland from Python: ctypes bindings for libei, libeis and liboeffis, plus the RemoteDesktop and InputCapture portals
|
|
5
5
|
Author: Dennis K. Paulsen
|
|
6
6
|
License-Expression: MIT
|
|
@@ -40,6 +40,7 @@ Dynamic: license-file
|
|
|
40
40
|
|
|
41
41
|
# python-libei
|
|
42
42
|
|
|
43
|
+
[](https://github.com/ctrondlp/python-libei/blob/main/CONTRIBUTING.md)
|
|
43
44
|
[](https://github.com/ctrondlp/python-libei/actions/workflows/ci.yml)
|
|
44
45
|
[](https://pypi.org/project/python-libei/)
|
|
45
46
|
[](https://github.com/ctrondlp/python-libei/blob/main/LICENSE)
|
|
@@ -174,7 +175,7 @@ and why, is in
|
|
|
174
175
|
|
|
175
176
|
## Status
|
|
176
177
|
|
|
177
|
-
Beta (`0.
|
|
178
|
+
Beta (`0.7.0`), published on [PyPI](https://pypi.org/project/python-libei/)
|
|
178
179
|
since `0.1.0`, and **the API is not frozen** — expect renames before 1.0.
|
|
179
180
|
|
|
180
181
|
The injection path is exercised end to end against the real libraries by the
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
libei/__init__.py,sha256=
|
|
1
|
+
libei/__init__.py,sha256=3Ru2esZsveT5oIlBPJ_6DH8RsRoofVvvf27WZafcOZg,1858
|
|
2
2
|
libei/_cobject.py,sha256=XjbUNJpZ87jAyPWh6dmRslk6cMy831oMRFhWmfjLDFo,13344
|
|
3
3
|
libei/ei.py,sha256=W4akKc_BNVfL0FroSXLGD1Z_L3OmYjZT5QDHNnEtMP8,49367
|
|
4
4
|
libei/eis.py,sha256=X_umAaAAbAMC9d2RdnMReeJZI61WsnKz3cySY5YrGr0,45841
|
|
5
5
|
libei/oeffis.py,sha256=gJHR90s9VhQrgkjWUMDx-lxZ8qKxkttnqbx_xo-7oxs,10035
|
|
6
|
-
libei/portal.py,sha256=
|
|
6
|
+
libei/portal.py,sha256=Q2GbZBUnKJZ90WJtDtpkcgQbvsCLY6xJwngmYRg5qQ0,72089
|
|
7
7
|
libei/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
libei/_capi/__init__.py,sha256=raISSWSV9hAcE92DcLCz-vUb1IPcsmxRZaS-iAoQJBE,211
|
|
9
9
|
libei/_capi/libei.py,sha256=qsccHa_komCxtvPYi6AobszhFbEG2qA8QYJJVr8SaGM,11356
|
|
10
10
|
libei/_capi/libeis.py,sha256=C_Uh7DwNncFC8A4wuMhviAj5C_TUmKHtLJYq0Qhhz1E,13142
|
|
11
11
|
libei/_capi/liboeffis.py,sha256=V8jdmJm3qOQUzzNiCAXt4Jr2JY11Y4-w6NIfIxbxGI4,1218
|
|
12
12
|
libei/_capi/loader.py,sha256=xW67uk35ckgeFaheDGrHC05ZM5w_cxBGvX5lG_GUSu8,6625
|
|
13
|
-
python_libei-0.
|
|
14
|
-
python_libei-0.
|
|
15
|
-
python_libei-0.
|
|
16
|
-
python_libei-0.
|
|
17
|
-
python_libei-0.
|
|
13
|
+
python_libei-0.7.0.dist-info/licenses/LICENSE,sha256=l6xbMU6Y-JZDzmBciBj-J5t6h6jNg_Bcyp4bHxDepqU,1074
|
|
14
|
+
python_libei-0.7.0.dist-info/METADATA,sha256=kCY_c4mpI43wAk_72MFq1yyv3VZm8r9NBaexaQzgWCg,22882
|
|
15
|
+
python_libei-0.7.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
16
|
+
python_libei-0.7.0.dist-info/top_level.txt,sha256=_DQXzGjDsUBENI_cNkiOxPB4xi8coCbQS1lq18FMudQ,6
|
|
17
|
+
python_libei-0.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|