aioxlib 0.0.3__tar.gz → 0.0.5__tar.gz
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.
- {aioxlib-0.0.3 → aioxlib-0.0.5}/PKG-INFO +8 -4
- {aioxlib-0.0.3 → aioxlib-0.0.5}/README.md +7 -3
- {aioxlib-0.0.3 → aioxlib-0.0.5}/aioxlib/__init__.py +230 -68
- {aioxlib-0.0.3 → aioxlib-0.0.5}/aioxlib.egg-info/PKG-INFO +8 -4
- {aioxlib-0.0.3 → aioxlib-0.0.5}/aioxlib.egg-info/SOURCES.txt +2 -1
- {aioxlib-0.0.3 → aioxlib-0.0.5}/pyproject.toml +18 -0
- aioxlib-0.0.5/tests/test_aioxlib.py +3589 -0
- {aioxlib-0.0.3 → aioxlib-0.0.5}/aioxlib.egg-info/dependency_links.txt +0 -0
- {aioxlib-0.0.3 → aioxlib-0.0.5}/aioxlib.egg-info/top_level.txt +0 -0
- {aioxlib-0.0.3 → aioxlib-0.0.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aioxlib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Human friendly interface to XLib subsystems using asyncio python
|
|
5
5
|
Author-email: Jose Tiago Macara Coutinho <coutinhotiago@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -58,6 +58,7 @@ pip install aioxlib
|
|
|
58
58
|
import asyncio
|
|
59
59
|
import aioxlib
|
|
60
60
|
|
|
61
|
+
|
|
61
62
|
async def main():
|
|
62
63
|
display = await aioxlib.get_display()
|
|
63
64
|
async with display:
|
|
@@ -72,11 +73,14 @@ async def main():
|
|
|
72
73
|
await wnd.set_wm_protocols([delete])
|
|
73
74
|
|
|
74
75
|
async for event in display.events():
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
if (
|
|
77
|
+
event["code"] == aioxlib.Event.ClientMessage
|
|
78
|
+
and event["type"] == protocols
|
|
79
|
+
and event["data"][0] == delete
|
|
80
|
+
):
|
|
78
81
|
break
|
|
79
82
|
|
|
83
|
+
|
|
80
84
|
asyncio.run(main())
|
|
81
85
|
```
|
|
82
86
|
|
|
@@ -34,6 +34,7 @@ pip install aioxlib
|
|
|
34
34
|
import asyncio
|
|
35
35
|
import aioxlib
|
|
36
36
|
|
|
37
|
+
|
|
37
38
|
async def main():
|
|
38
39
|
display = await aioxlib.get_display()
|
|
39
40
|
async with display:
|
|
@@ -48,11 +49,14 @@ async def main():
|
|
|
48
49
|
await wnd.set_wm_protocols([delete])
|
|
49
50
|
|
|
50
51
|
async for event in display.events():
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
if (
|
|
53
|
+
event["code"] == aioxlib.Event.ClientMessage
|
|
54
|
+
and event["type"] == protocols
|
|
55
|
+
and event["data"][0] == delete
|
|
56
|
+
):
|
|
54
57
|
break
|
|
55
58
|
|
|
59
|
+
|
|
56
60
|
asyncio.run(main())
|
|
57
61
|
```
|
|
58
62
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Copyright (c) 2026 Tiago Coutinho
|
|
5
5
|
# Distributed under the GPLv3 license. See LICENSE for more info.
|
|
6
6
|
|
|
7
|
-
__version__ = "0.0.
|
|
7
|
+
__version__ = "0.0.5"
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
10
|
import enum
|
|
@@ -27,9 +27,7 @@ PROTOCOL_MINOR = 0
|
|
|
27
27
|
|
|
28
28
|
TYPE = b"MIT-MAGIC-COOKIE-1"
|
|
29
29
|
SUPPORTED_PROTOCOLS = "unix", "tcp"
|
|
30
|
-
DISPLAY_RE = re.compile(
|
|
31
|
-
r"^((?P<protocol>tcp|unix)/)?(?P<host>[-a-zA-Z0-9._]*):(?P<port>\d+)(\.(?P<screen>\d+))?$"
|
|
32
|
-
)
|
|
30
|
+
DISPLAY_RE = re.compile(r"^((?P<protocol>tcp|unix)/)?(?P<host>[-a-zA-Z0-9._]*):(?P<port>\d+)(\.(?P<screen>\d+))?$")
|
|
33
31
|
|
|
34
32
|
BASE_TCP_PORT = 6000
|
|
35
33
|
UNIX_PATH_TEMPLATE = "/tmp/.X11-unix/X{}"
|
|
@@ -541,10 +539,12 @@ YSorted = 1
|
|
|
541
539
|
YXSorted = 2
|
|
542
540
|
YXBanded = 3
|
|
543
541
|
|
|
542
|
+
|
|
544
543
|
class CoordinateMode(enum.IntEnum):
|
|
545
544
|
Origin = 0
|
|
546
545
|
Previous = 1
|
|
547
546
|
|
|
547
|
+
|
|
548
548
|
Complex = 0
|
|
549
549
|
Nonconvex = 1
|
|
550
550
|
Convex = 2
|
|
@@ -698,7 +698,7 @@ FONT_FIELDS = (
|
|
|
698
698
|
def parse_font_name(name):
|
|
699
699
|
parts = name.split("-")[1:15]
|
|
700
700
|
result = {"name": name}
|
|
701
|
-
for key, value in zip(FONT_FIELDS, parts):
|
|
701
|
+
for key, value in zip(FONT_FIELDS, parts, strict=True):
|
|
702
702
|
if value == "":
|
|
703
703
|
result[key] = None
|
|
704
704
|
else:
|
|
@@ -977,7 +977,7 @@ def pack_list_properties(wid) -> bytes:
|
|
|
977
977
|
|
|
978
978
|
def unpack_list_properties(data, offset=0):
|
|
979
979
|
offset += 8
|
|
980
|
-
nb_atoms, = struct.unpack_from("H", data, offset)
|
|
980
|
+
(nb_atoms,) = struct.unpack_from("H", data, offset)
|
|
981
981
|
offset += 2 + 22
|
|
982
982
|
return struct.unpack_from(f"{nb_atoms}I", data, offset), offset + nb_atoms * 4
|
|
983
983
|
|
|
@@ -989,7 +989,7 @@ def pack_get_font_path():
|
|
|
989
989
|
def unpack_get_font_path(data, offset=0):
|
|
990
990
|
result, offset = unpack_reply_head(data, offset)
|
|
991
991
|
assert result["reply"] == 1
|
|
992
|
-
n, = struct.unpack_from("H", data, offset)
|
|
992
|
+
(n,) = struct.unpack_from("H", data, offset)
|
|
993
993
|
offset += 2 + 22
|
|
994
994
|
result["path"], offset = unpack_list_strings(data, n, offset)
|
|
995
995
|
return result, offset + 32 + result["length"] * 4
|
|
@@ -1000,9 +1000,7 @@ def pack_intern_atom(name, only_if_exists=False):
|
|
|
1000
1000
|
n = len(name)
|
|
1001
1001
|
p = pad(n)
|
|
1002
1002
|
length = 2 + (n + p) // 4
|
|
1003
|
-
return struct.pack(
|
|
1004
|
-
f"BBHH2x{n+p}s", Command.InternAtom, int(only_if_exists), length, n, name
|
|
1005
|
-
)
|
|
1003
|
+
return struct.pack(f"BBHH2x{n + p}s", Command.InternAtom, int(only_if_exists), length, n, name)
|
|
1006
1004
|
|
|
1007
1005
|
|
|
1008
1006
|
def pack_poly_fill_rectangles(drawable, gc, rectangles) -> bytes:
|
|
@@ -1052,7 +1050,7 @@ def pack_open_font(fid, name, encoding=DEFAULT_STRING_ENCODING):
|
|
|
1052
1050
|
length = 3 + (n + p) // 4
|
|
1053
1051
|
|
|
1054
1052
|
return struct.pack(
|
|
1055
|
-
f"BxHIHxx{n+p}s",
|
|
1053
|
+
f"BxHIHxx{n + p}s",
|
|
1056
1054
|
Command.OpenFont, # OpenFont
|
|
1057
1055
|
length,
|
|
1058
1056
|
fid,
|
|
@@ -1147,9 +1145,7 @@ def pack_create_gc(cid, drawable, **kwargs):
|
|
|
1147
1145
|
fmts.append(fmt)
|
|
1148
1146
|
vals.append(val)
|
|
1149
1147
|
length = 4 + len(kwargs)
|
|
1150
|
-
return struct.pack(
|
|
1151
|
-
"".join(fmts), Command.CreateGC, length, cid, drawable, mask, *vals
|
|
1152
|
-
)
|
|
1148
|
+
return struct.pack("".join(fmts), Command.CreateGC, length, cid, drawable, mask, *vals)
|
|
1153
1149
|
|
|
1154
1150
|
|
|
1155
1151
|
def pack_free_gc(gc):
|
|
@@ -1224,7 +1220,7 @@ def pack_query_extension(name) -> bytes:
|
|
|
1224
1220
|
n = len(name)
|
|
1225
1221
|
p = pad(n)
|
|
1226
1222
|
length = 2 + (n + p) // 4
|
|
1227
|
-
return struct.pack(f"BxHH2x{n+p}s", Command.QueryExtension, length, n, name)
|
|
1223
|
+
return struct.pack(f"BxHH2x{n + p}s", Command.QueryExtension, length, n, name)
|
|
1228
1224
|
|
|
1229
1225
|
|
|
1230
1226
|
def unpack_query_extension(data: bytes, offset: int = 0):
|
|
@@ -1241,7 +1237,7 @@ def pack_list_fonts(pattern, nmax=2**16):
|
|
|
1241
1237
|
pattern = pattern.encode(DEFAULT_STRING_ENCODING)
|
|
1242
1238
|
n = len(pattern)
|
|
1243
1239
|
p = pad(n)
|
|
1244
|
-
fmt = f"BxBHH{n+p}s"
|
|
1240
|
+
fmt = f"BxBHH{n + p}s"
|
|
1245
1241
|
length = 2 + (n + p) // 4
|
|
1246
1242
|
return struct.pack(fmt, Command.ListFonts, length, nmax, n, pattern)
|
|
1247
1243
|
|
|
@@ -1261,7 +1257,7 @@ def pack_image_text(wnd, gc, x, y, text):
|
|
|
1261
1257
|
n = len(text)
|
|
1262
1258
|
p = pad(n)
|
|
1263
1259
|
length = 4 + (n + p) // 4
|
|
1264
|
-
fmt = f"BBHIIhh{n+p}s"
|
|
1260
|
+
fmt = f"BBHIIhh{n + p}s"
|
|
1265
1261
|
return struct.pack(fmt, Command.ImageText8, n, length, wnd, gc, x, y, text)
|
|
1266
1262
|
|
|
1267
1263
|
|
|
@@ -1270,9 +1266,7 @@ def pack_image(format, drawable, gc, w, h, x, y, left_pad, depth, data):
|
|
|
1270
1266
|
n = len(data)
|
|
1271
1267
|
p = pad(n)
|
|
1272
1268
|
length = 6 + (n + p) // 4
|
|
1273
|
-
head = struct.pack(
|
|
1274
|
-
fmt, 72, format, length, drawable, gc, w, h, x, y, left_pad, depth
|
|
1275
|
-
)
|
|
1269
|
+
head = struct.pack(fmt, 72, format, length, drawable, gc, w, h, x, y, left_pad, depth)
|
|
1276
1270
|
return head, data, p * b"\x00"
|
|
1277
1271
|
|
|
1278
1272
|
|
|
@@ -1312,9 +1306,7 @@ def get_auth_config(filename: str) -> list[dict]:
|
|
|
1312
1306
|
offset += 2
|
|
1313
1307
|
data = raw[offset : offset + datalen]
|
|
1314
1308
|
offset += datalen
|
|
1315
|
-
entries.append(
|
|
1316
|
-
{"family": family, "address": addr, "port": num, "name": name, "data": data}
|
|
1317
|
-
)
|
|
1309
|
+
entries.append({"family": family, "address": addr, "port": num, "name": name, "data": data})
|
|
1318
1310
|
return entries
|
|
1319
1311
|
|
|
1320
1312
|
|
|
@@ -1468,9 +1460,7 @@ class Window(Drawable):
|
|
|
1468
1460
|
async def set_name(self, name):
|
|
1469
1461
|
await self.change_text_property(Atom.WM_NAME, name)
|
|
1470
1462
|
|
|
1471
|
-
async def change_text_property(
|
|
1472
|
-
self, property, data, property_type=Atom.STRING, mode=PropMode.Replace
|
|
1473
|
-
):
|
|
1463
|
+
async def change_text_property(self, property, data, property_type=Atom.STRING, mode=PropMode.Replace):
|
|
1474
1464
|
if not isinstance(data, bytes):
|
|
1475
1465
|
if property_type == Atom.STRING:
|
|
1476
1466
|
data = data.encode(DEFAULT_STRING_ENCODING)
|
|
@@ -1490,12 +1480,8 @@ class Window(Drawable):
|
|
|
1490
1480
|
async def destroy(self):
|
|
1491
1481
|
return await self.display.destroy_window(self.id)
|
|
1492
1482
|
|
|
1493
|
-
async def change_property(
|
|
1494
|
-
self, property, property_type, format, data, mode
|
|
1495
|
-
):
|
|
1496
|
-
payload = pack_change_property(
|
|
1497
|
-
self.id, property, property_type, format, data, mode
|
|
1498
|
-
)
|
|
1483
|
+
async def change_property(self, property, property_type, format, data, mode=PropMode.Replace):
|
|
1484
|
+
payload = pack_change_property(self.id, property, property_type, format, data, mode)
|
|
1499
1485
|
self.display.connection.write(payload)
|
|
1500
1486
|
|
|
1501
1487
|
async def set_wm_protocols(self, protocols):
|
|
@@ -1509,7 +1495,7 @@ class Window(Drawable):
|
|
|
1509
1495
|
return await self.display.list_properties(self)
|
|
1510
1496
|
|
|
1511
1497
|
async def clear_area(self, x, y, w, h, exposure=False):
|
|
1512
|
-
await self.display.clear_area(self
|
|
1498
|
+
await self.display.clear_area(self, x, y, w, h, exposure)
|
|
1513
1499
|
|
|
1514
1500
|
async def clear(self, exposure=0):
|
|
1515
1501
|
await self.clear_area(0, 0, 0, 0, exposure)
|
|
@@ -1539,19 +1525,6 @@ class GC(Resource):
|
|
|
1539
1525
|
self.display.connection.write(payload)
|
|
1540
1526
|
|
|
1541
1527
|
|
|
1542
|
-
class LogBytes:
|
|
1543
|
-
__slots__ = ["data"]
|
|
1544
|
-
Limit = 32
|
|
1545
|
-
|
|
1546
|
-
def __init__(self, data):
|
|
1547
|
-
self.data = data
|
|
1548
|
-
|
|
1549
|
-
def __str__(self):
|
|
1550
|
-
if len(self.data) > self.Limit:
|
|
1551
|
-
return " ".join(f"{b:02X}" for b in self.data[: self.Limit]) + " [...]"
|
|
1552
|
-
return " ".join(f"{b:02X}" for b in self.data)
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
1528
|
class Connection:
|
|
1556
1529
|
def __init__(self, reader, writer, config):
|
|
1557
1530
|
self.reader = reader
|
|
@@ -1586,13 +1559,6 @@ class Connection:
|
|
|
1586
1559
|
self.writer.write(payload)
|
|
1587
1560
|
|
|
1588
1561
|
def writelines(self, lines):
|
|
1589
|
-
l0 = lines[0]
|
|
1590
|
-
code = l0[0]
|
|
1591
|
-
if code in {LITTLE_ENDIAN, BIG_ENDIAN}:
|
|
1592
|
-
cmd_name = "Setup"
|
|
1593
|
-
else:
|
|
1594
|
-
cmd_name = Command(code).name
|
|
1595
|
-
log.debug("WRITEL(%3d): %s %s", len(lines), cmd_name, LogBytes(l0))
|
|
1596
1562
|
self.writer.writelines(lines)
|
|
1597
1563
|
|
|
1598
1564
|
async def flush(self):
|
|
@@ -1646,11 +1612,7 @@ class Xauthority:
|
|
|
1646
1612
|
ename, eport = entry["name"], entry["port"]
|
|
1647
1613
|
if not eport and entry["name"] not in matches:
|
|
1648
1614
|
eport = bport
|
|
1649
|
-
if
|
|
1650
|
-
entry["family"] == family
|
|
1651
|
-
and entry["address"] == address
|
|
1652
|
-
and eport == bport
|
|
1653
|
-
):
|
|
1615
|
+
if entry["family"] == family and entry["address"] == address and eport == bport:
|
|
1654
1616
|
matches[ename] = entry
|
|
1655
1617
|
for type_ in types:
|
|
1656
1618
|
try:
|
|
@@ -1708,9 +1670,7 @@ def pack_shm_attach(opcode, rid, shmid, read_only=True):
|
|
|
1708
1670
|
def pack_shm_attach_fd(opcode, fd, read_only=True):
|
|
1709
1671
|
fmt = "BBHIB3x"
|
|
1710
1672
|
length = struct.calcsize(fmt) // 4
|
|
1711
|
-
return struct.pack(
|
|
1712
|
-
fmt, opcode, SharedMemoryOperation.AttachFd, length, fd, 1 if read_only else 0
|
|
1713
|
-
)
|
|
1673
|
+
return struct.pack(fmt, opcode, SharedMemoryOperation.AttachFd, length, fd, 1 if read_only else 0)
|
|
1714
1674
|
|
|
1715
1675
|
|
|
1716
1676
|
def pack_shm_version(opcode):
|
|
@@ -1858,7 +1818,213 @@ class SharedMemory(Extension):
|
|
|
1858
1818
|
self.display.write(payload)
|
|
1859
1819
|
|
|
1860
1820
|
|
|
1861
|
-
|
|
1821
|
+
class DoubleBufferOperation(enum.IntEnum):
|
|
1822
|
+
GetVersion = 0
|
|
1823
|
+
AllocateBackBufferName = 1
|
|
1824
|
+
DeallocateBackBufferName = 2
|
|
1825
|
+
SwapBuffers = 3
|
|
1826
|
+
BeginIdiom = 4
|
|
1827
|
+
EndIdiom = 5
|
|
1828
|
+
GetVisualInfo = 6
|
|
1829
|
+
GetBackBufferAttributes = 7
|
|
1830
|
+
|
|
1831
|
+
|
|
1832
|
+
class SwapAction(enum.IntEnum):
|
|
1833
|
+
"""What to do with the new back buffer after a swap."""
|
|
1834
|
+
|
|
1835
|
+
Undefined = 0
|
|
1836
|
+
Background = 1
|
|
1837
|
+
Untouched = 2
|
|
1838
|
+
Copied = 3
|
|
1839
|
+
|
|
1840
|
+
|
|
1841
|
+
def pack_dbe_get_version(opcode, client_major=1, client_minor=0):
|
|
1842
|
+
return struct.pack(
|
|
1843
|
+
"BBHBBH",
|
|
1844
|
+
opcode,
|
|
1845
|
+
DoubleBufferOperation.GetVersion,
|
|
1846
|
+
2,
|
|
1847
|
+
client_major,
|
|
1848
|
+
client_minor,
|
|
1849
|
+
0,
|
|
1850
|
+
)
|
|
1851
|
+
|
|
1852
|
+
|
|
1853
|
+
def unpack_dbe_get_version(data, offset=0):
|
|
1854
|
+
result, offset = unpack_reply_head(data, offset)
|
|
1855
|
+
major, minor = struct.unpack_from("=BB", data, offset)
|
|
1856
|
+
offset += 2 + 22
|
|
1857
|
+
result["major_version"] = major
|
|
1858
|
+
result["minor_version"] = minor
|
|
1859
|
+
return result, offset
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
def pack_dbe_allocate_back_buffer(opcode, window, buffer, swap_action=SwapAction.Undefined):
|
|
1863
|
+
return struct.pack(
|
|
1864
|
+
"BBHIIB3x",
|
|
1865
|
+
opcode,
|
|
1866
|
+
DoubleBufferOperation.AllocateBackBufferName,
|
|
1867
|
+
4,
|
|
1868
|
+
int(window),
|
|
1869
|
+
int(buffer),
|
|
1870
|
+
int(swap_action),
|
|
1871
|
+
)
|
|
1872
|
+
|
|
1873
|
+
|
|
1874
|
+
def pack_dbe_deallocate_back_buffer(opcode, buffer):
|
|
1875
|
+
return struct.pack(
|
|
1876
|
+
"BBHI",
|
|
1877
|
+
opcode,
|
|
1878
|
+
DoubleBufferOperation.DeallocateBackBufferName,
|
|
1879
|
+
2,
|
|
1880
|
+
int(buffer),
|
|
1881
|
+
)
|
|
1882
|
+
|
|
1883
|
+
|
|
1884
|
+
def pack_dbe_swap_buffers(opcode, actions):
|
|
1885
|
+
"""actions: sequence of (window, swap_action) pairs."""
|
|
1886
|
+
n = len(actions)
|
|
1887
|
+
length = 2 + 2 * n
|
|
1888
|
+
parts = [struct.pack("=BBHI", opcode, DoubleBufferOperation.SwapBuffers, length, n)]
|
|
1889
|
+
for window, action in actions:
|
|
1890
|
+
parts.append(struct.pack("=IB3x", int(window), int(action)))
|
|
1891
|
+
return b"".join(parts)
|
|
1892
|
+
|
|
1893
|
+
|
|
1894
|
+
def pack_dbe_begin_idiom(opcode):
|
|
1895
|
+
return struct.pack("=BBH", opcode, DoubleBufferOperation.BeginIdiom, 1)
|
|
1896
|
+
|
|
1897
|
+
|
|
1898
|
+
def pack_dbe_end_idiom(opcode):
|
|
1899
|
+
return struct.pack("=BBH", opcode, DoubleBufferOperation.EndIdiom, 1)
|
|
1900
|
+
|
|
1901
|
+
|
|
1902
|
+
def pack_dbe_get_visual_info(opcode, drawables):
|
|
1903
|
+
"""drawables: sequence of drawable ids (empty = all screens)."""
|
|
1904
|
+
n = len(drawables)
|
|
1905
|
+
length = 2 + n
|
|
1906
|
+
header = struct.pack("=BBHI", opcode, DoubleBufferOperation.GetVisualInfo, length, n)
|
|
1907
|
+
body = b"".join(struct.pack("=I", int(d)) for d in drawables)
|
|
1908
|
+
return header + body
|
|
1909
|
+
|
|
1910
|
+
|
|
1911
|
+
def unpack_dbe_get_visual_info(data, offset=0):
|
|
1912
|
+
result, offset = unpack_reply_head(data, offset)
|
|
1913
|
+
(n_screens,) = struct.unpack_from("=I", data, offset)
|
|
1914
|
+
offset += 4 + 20
|
|
1915
|
+
screens = []
|
|
1916
|
+
for _ in range(n_screens):
|
|
1917
|
+
(n_infos,) = struct.unpack_from("=I", data, offset)
|
|
1918
|
+
offset += 4
|
|
1919
|
+
infos = []
|
|
1920
|
+
for _ in range(n_infos):
|
|
1921
|
+
visual, depth, perflevel = struct.unpack_from("=IBB2x", data, offset)
|
|
1922
|
+
offset += 8
|
|
1923
|
+
infos.append({"visual": visual, "depth": depth, "perflevel": perflevel})
|
|
1924
|
+
screens.append(infos)
|
|
1925
|
+
result["screens"] = screens
|
|
1926
|
+
return result, offset
|
|
1927
|
+
|
|
1928
|
+
|
|
1929
|
+
def pack_dbe_get_back_buffer_attributes(opcode, buffer):
|
|
1930
|
+
return struct.pack(
|
|
1931
|
+
"BBHI",
|
|
1932
|
+
opcode,
|
|
1933
|
+
DoubleBufferOperation.GetBackBufferAttributes,
|
|
1934
|
+
2,
|
|
1935
|
+
int(buffer),
|
|
1936
|
+
)
|
|
1937
|
+
|
|
1938
|
+
|
|
1939
|
+
def unpack_dbe_get_back_buffer_attributes(data, offset=0):
|
|
1940
|
+
result, offset = unpack_reply_head(data, offset)
|
|
1941
|
+
(window,) = struct.unpack_from("=I", data, offset)
|
|
1942
|
+
offset += 4 + 20
|
|
1943
|
+
result["window"] = window
|
|
1944
|
+
return result, offset
|
|
1945
|
+
|
|
1946
|
+
|
|
1947
|
+
class BackBuffer(Drawable):
|
|
1948
|
+
def __init__(self, bid, window, extension):
|
|
1949
|
+
super().__init__(extension.display, bid)
|
|
1950
|
+
self.window = window
|
|
1951
|
+
self.extension = extension
|
|
1952
|
+
|
|
1953
|
+
async def swap(self, swap_action=SwapAction.Undefined):
|
|
1954
|
+
return await self.extension.swap(self.window, swap_action)
|
|
1955
|
+
|
|
1956
|
+
async def __aenter__(self):
|
|
1957
|
+
return self
|
|
1958
|
+
|
|
1959
|
+
async def __aexit__(self, *_):
|
|
1960
|
+
await self.extension.deallocate_back_buffer(self.id)
|
|
1961
|
+
|
|
1962
|
+
|
|
1963
|
+
class DoubleBuffer(Extension):
|
|
1964
|
+
"""X Double Buffer Extension (DOUBLE-BUFFER / DBE).
|
|
1965
|
+
|
|
1966
|
+
Provides front/back buffers for flicker-free animation. Query with::
|
|
1967
|
+
|
|
1968
|
+
dbe = await display.query_extension("DOUBLE-BUFFER")
|
|
1969
|
+
"""
|
|
1970
|
+
|
|
1971
|
+
BaseBadBuffer = 0
|
|
1972
|
+
|
|
1973
|
+
def __init__(self, display, info):
|
|
1974
|
+
super().__init__(display, info)
|
|
1975
|
+
self.BadBuffer = info["base_error"] + self.BaseBadBuffer
|
|
1976
|
+
self.errors = {self.BadBuffer: "BadBuffer"}
|
|
1977
|
+
self.commands = {self.code: "Dbe"}
|
|
1978
|
+
|
|
1979
|
+
async def version(self, client_major=1, client_minor=0):
|
|
1980
|
+
payload = pack_dbe_get_version(self.code, client_major, client_minor)
|
|
1981
|
+
return await self.display.write_read(payload, unpack_dbe_get_version)
|
|
1982
|
+
|
|
1983
|
+
async def allocate_back_buffer(self, window, swap_action=SwapAction.Undefined, buffer=None):
|
|
1984
|
+
"""Allocate a back-buffer XID for *window*.
|
|
1985
|
+
|
|
1986
|
+
Returns the back-buffer resource id (drawable usable with core
|
|
1987
|
+
drawing requests).
|
|
1988
|
+
"""
|
|
1989
|
+
if buffer is None:
|
|
1990
|
+
buffer = self.display.resources.allocate()
|
|
1991
|
+
payload = pack_dbe_allocate_back_buffer(self.code, window, buffer, swap_action)
|
|
1992
|
+
self.display.write(payload)
|
|
1993
|
+
return BackBuffer(buffer, window, self)
|
|
1994
|
+
|
|
1995
|
+
async def deallocate_back_buffer(self, buffer):
|
|
1996
|
+
payload = pack_dbe_deallocate_back_buffer(self.code, buffer)
|
|
1997
|
+
self.display.write(payload)
|
|
1998
|
+
|
|
1999
|
+
async def swap_buffers(self, actions):
|
|
2000
|
+
"""Swap front/back buffers.
|
|
2001
|
+
|
|
2002
|
+
*actions* is a sequence of ``(window, swap_action)`` pairs.
|
|
2003
|
+
"""
|
|
2004
|
+
payload = pack_dbe_swap_buffers(self.code, actions)
|
|
2005
|
+
self.display.write(payload)
|
|
2006
|
+
|
|
2007
|
+
async def swap(self, window, swap_action=SwapAction.Undefined):
|
|
2008
|
+
"""Convenience: swap a single window."""
|
|
2009
|
+
await self.swap_buffers([(window, swap_action)])
|
|
2010
|
+
|
|
2011
|
+
async def begin_idiom(self):
|
|
2012
|
+
self.display.write(pack_dbe_begin_idiom(self.code))
|
|
2013
|
+
|
|
2014
|
+
async def end_idiom(self):
|
|
2015
|
+
self.display.write(pack_dbe_end_idiom(self.code))
|
|
2016
|
+
|
|
2017
|
+
async def get_visual_info(self, drawables=()):
|
|
2018
|
+
"""Return per-screen lists of visuals that support double buffering."""
|
|
2019
|
+
payload = pack_dbe_get_visual_info(self.code, drawables)
|
|
2020
|
+
return await self.display.write_read(payload, unpack_dbe_get_visual_info)
|
|
2021
|
+
|
|
2022
|
+
async def get_back_buffer_attributes(self, buffer):
|
|
2023
|
+
payload = pack_dbe_get_back_buffer_attributes(self.code, buffer)
|
|
2024
|
+
return await self.display.write_read(payload, unpack_dbe_get_back_buffer_attributes)
|
|
2025
|
+
|
|
2026
|
+
|
|
2027
|
+
EXTENSIONS = {"MIT-SHM": SharedMemory, "DOUBLE-BUFFER": DoubleBuffer}
|
|
1862
2028
|
|
|
1863
2029
|
|
|
1864
2030
|
class Pipeline:
|
|
@@ -1956,9 +2122,7 @@ class Display:
|
|
|
1956
2122
|
await self.flush()
|
|
1957
2123
|
info = await read_setup(self)
|
|
1958
2124
|
self.info = info
|
|
1959
|
-
self.resources = ResourceManager(
|
|
1960
|
-
info["resource_id_base"], info["resource_id_mask"]
|
|
1961
|
-
)
|
|
2125
|
+
self.resources = ResourceManager(info["resource_id_base"], info["resource_id_mask"])
|
|
1962
2126
|
self.screens = [Screen(self, sinfo) for sinfo in info["screens"]]
|
|
1963
2127
|
self._task = asyncio.create_task(self._loop())
|
|
1964
2128
|
return info
|
|
@@ -2011,9 +2175,7 @@ class Display:
|
|
|
2011
2175
|
return self._atom_cache[name]
|
|
2012
2176
|
|
|
2013
2177
|
atom = await self.intern_atom(name, only_if_exists)
|
|
2014
|
-
if
|
|
2015
|
-
atom != NONE
|
|
2016
|
-
): # don't cache NONE responses in case someone creates this later
|
|
2178
|
+
if atom != NONE: # don't cache NONE responses in case someone creates this later
|
|
2017
2179
|
self._atom_cache[name] = atom
|
|
2018
2180
|
return atom
|
|
2019
2181
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aioxlib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Human friendly interface to XLib subsystems using asyncio python
|
|
5
5
|
Author-email: Jose Tiago Macara Coutinho <coutinhotiago@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -58,6 +58,7 @@ pip install aioxlib
|
|
|
58
58
|
import asyncio
|
|
59
59
|
import aioxlib
|
|
60
60
|
|
|
61
|
+
|
|
61
62
|
async def main():
|
|
62
63
|
display = await aioxlib.get_display()
|
|
63
64
|
async with display:
|
|
@@ -72,11 +73,14 @@ async def main():
|
|
|
72
73
|
await wnd.set_wm_protocols([delete])
|
|
73
74
|
|
|
74
75
|
async for event in display.events():
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
if (
|
|
77
|
+
event["code"] == aioxlib.Event.ClientMessage
|
|
78
|
+
and event["type"] == protocols
|
|
79
|
+
and event["data"][0] == delete
|
|
80
|
+
):
|
|
78
81
|
break
|
|
79
82
|
|
|
83
|
+
|
|
80
84
|
asyncio.run(main())
|
|
81
85
|
```
|
|
82
86
|
|
|
@@ -49,9 +49,16 @@ docs = [
|
|
|
49
49
|
"pymdown-extensions",
|
|
50
50
|
"mkdocs-coverage",
|
|
51
51
|
]
|
|
52
|
+
test = [
|
|
53
|
+
"pytest>=7",
|
|
54
|
+
"pytest-cov>=4",
|
|
55
|
+
"coverage>=7",
|
|
56
|
+
"pytest-asyncio>=1.4",
|
|
57
|
+
]
|
|
52
58
|
dev = [
|
|
53
59
|
{include-group = "build-deploy"},
|
|
54
60
|
{include-group = "docs"},
|
|
61
|
+
{include-group = "test"},
|
|
55
62
|
]
|
|
56
63
|
|
|
57
64
|
[project.scripts]
|
|
@@ -89,3 +96,14 @@ target-version = "py312"
|
|
|
89
96
|
|
|
90
97
|
[tool.ruff.lint.isort]
|
|
91
98
|
combine-as-imports = true
|
|
99
|
+
|
|
100
|
+
[tool.pytest.ini_options]
|
|
101
|
+
minversion = "7.0"
|
|
102
|
+
testpaths = ["tests"]
|
|
103
|
+
addopts = [
|
|
104
|
+
"-ra",
|
|
105
|
+
"--strict-markers",
|
|
106
|
+
"--cov=aioxlib",
|
|
107
|
+
"--cov-report=term-missing",
|
|
108
|
+
"--cov-report=html"
|
|
109
|
+
]
|