aioxlib 0.4.0__tar.gz → 0.5.0__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.
Files changed (39) hide show
  1. {aioxlib-0.4.0 → aioxlib-0.5.0}/PKG-INFO +4 -2
  2. {aioxlib-0.4.0 → aioxlib-0.5.0}/README.md +3 -1
  3. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/__init__.py +4 -7
  4. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/_extensions.py +2 -0
  5. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/api.py +8 -2
  6. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/connection.py +0 -4
  7. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/display.py +48 -7
  8. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/protocol.py +76 -0
  9. aioxlib-0.5.0/aioxlib/randr.py +1350 -0
  10. aioxlib-0.5.0/aioxlib/widgets.py +211 -0
  11. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib.egg-info/PKG-INFO +4 -2
  12. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib.egg-info/SOURCES.txt +2 -0
  13. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_connection.py +0 -16
  14. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/authority.py +0 -0
  15. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/bigreq.py +0 -0
  16. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/constants.py +0 -0
  17. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/dbe.py +0 -0
  18. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/dri3.py +0 -0
  19. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/glx.py +0 -0
  20. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/present.py +0 -0
  21. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/render.py +0 -0
  22. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib/shm.py +0 -0
  23. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib.egg-info/dependency_links.txt +0 -0
  24. {aioxlib-0.4.0 → aioxlib-0.5.0}/aioxlib.egg-info/top_level.txt +0 -0
  25. {aioxlib-0.4.0 → aioxlib-0.5.0}/pyproject.toml +0 -0
  26. {aioxlib-0.4.0 → aioxlib-0.5.0}/setup.cfg +0 -0
  27. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_api.py +0 -0
  28. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_authority.py +0 -0
  29. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_bigreq.py +0 -0
  30. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_constants.py +0 -0
  31. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_dbe.py +0 -0
  32. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_display.py +0 -0
  33. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_dri3.py +0 -0
  34. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_examples.py +0 -0
  35. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_glx.py +0 -0
  36. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_present.py +0 -0
  37. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_protocol.py +0 -0
  38. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_render.py +0 -0
  39. {aioxlib-0.4.0 → aioxlib-0.5.0}/tests/test_shm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aioxlib
3
- Version: 0.4.0
3
+ Version: 0.5.0
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
@@ -36,10 +36,12 @@ asyncio—without blocking the event loop and without linking to libX11.
36
36
  ## Documentation
37
37
 
38
38
  - Online: [aioxlib.readthedocs.io](https://aioxlib.readthedocs.io)
39
- - Local: `pip install -r docs/requirements.txt && mkdocs serve`
39
+ - Local: `pip install --group docs && mkdocs serve`
40
40
 
41
41
  ## Installation
42
42
 
43
+ From within your favorite python environment:
44
+
43
45
  ```bash
44
46
  pip install aioxlib
45
47
  ```
@@ -12,10 +12,12 @@ asyncio—without blocking the event loop and without linking to libX11.
12
12
  ## Documentation
13
13
 
14
14
  - Online: [aioxlib.readthedocs.io](https://aioxlib.readthedocs.io)
15
- - Local: `pip install -r docs/requirements.txt && mkdocs serve`
15
+ - Local: `pip install --group docs && mkdocs serve`
16
16
 
17
17
  ## Installation
18
18
 
19
+ From within your favorite python environment:
20
+
19
21
  ```bash
20
22
  pip install aioxlib
21
23
  ```
@@ -16,19 +16,16 @@ Types and protocol helpers live in submodules::
16
16
  from aioxlib.protocol import pack_create_window
17
17
  """
18
18
 
19
- __version__ = "0.4.0"
19
+ __version__ = "0.5.0"
20
20
 
21
+ from . import api, constants
21
22
  from .api import * # noqa: F403
22
- from .api import __all__ as _api_all
23
23
  from .constants import * # noqa: F403
24
- from .constants import __all__ as _constants_all
25
24
  from .display import get_display
26
25
 
27
26
  __all__ = [
28
27
  "__version__",
29
28
  "get_display",
30
- *_constants_all,
31
- *_api_all,
29
+ *constants.__all__,
30
+ *api.__all__,
32
31
  ]
33
-
34
- del _api_all, _constants_all
@@ -11,6 +11,7 @@ from .dbe import DoubleBuffer
11
11
  from .dri3 import DRI3
12
12
  from .glx import GLX
13
13
  from .present import Present
14
+ from .randr import RandR
14
15
  from .render import Render
15
16
  from .shm import SharedMemory
16
17
 
@@ -22,4 +23,5 @@ EXTENSIONS = {
22
23
  "DRI3": DRI3,
23
24
  "Present": Present,
24
25
  "RENDER": Render,
26
+ "RANDR": RandR,
25
27
  }
@@ -207,6 +207,11 @@ async def get_font_path(display: Display | None = None):
207
207
  return await d.get_font_path()
208
208
 
209
209
 
210
+ async def set_font_path(path, display: Display | None = None):
211
+ d = await _resolve_display(display)
212
+ return await d.set_font_path(path)
213
+
214
+
210
215
  async def get_atom(name, only_if_exists=False, display: Display | None = None):
211
216
  """Intern *name* (with cache) on the default (or given) display."""
212
217
  d = await _resolve_display(display)
@@ -296,10 +301,10 @@ async def open_font_name(name, display: Display | None = None):
296
301
  return await d.open_font_name(name)
297
302
 
298
303
 
299
- async def open_font(display: Display | None = None, **kwargs):
304
+ async def open_font(name=None, display: Display | None = None, **kwargs):
300
305
  """Open a font built from keyword XLFD fields."""
301
306
  d = await _resolve_display(display)
302
- return await d.open_font(**kwargs)
307
+ return await d.open_font(name=name, **kwargs)
303
308
 
304
309
 
305
310
  async def tree(wnd, display: Display | None = None):
@@ -402,6 +407,7 @@ __all__ = [
402
407
  "query_extension",
403
408
  "list_fonts",
404
409
  "get_font_path",
410
+ "set_font_path",
405
411
  "get_atom",
406
412
  "default_screen",
407
413
  "create_window",
@@ -97,10 +97,6 @@ class Connection:
97
97
  """Write."""
98
98
  self.writer.write(payload)
99
99
 
100
- def writelines(self, lines):
101
- """Writelines."""
102
- self.writer.writelines(lines)
103
-
104
100
  async def flush(self):
105
101
  """Flush."""
106
102
  return await self.writer.drain()
@@ -41,12 +41,15 @@ from .protocol import (
41
41
  pack_change_property,
42
42
  pack_clear_area,
43
43
  pack_configure_window,
44
+ pack_copy_area,
44
45
  pack_create_colormap,
45
46
  pack_create_gc,
47
+ pack_create_pixmap,
46
48
  pack_create_window,
47
49
  pack_delete_property,
48
50
  pack_destroy_window,
49
51
  pack_free_colormap,
52
+ pack_free_pixmap,
50
53
  pack_get_atom_name,
51
54
  pack_get_font_path,
52
55
  pack_get_geometry,
@@ -69,12 +72,14 @@ from .protocol import (
69
72
  pack_query_extension,
70
73
  pack_query_tree,
71
74
  pack_reparent_window,
75
+ pack_set_font_path,
72
76
  pack_set_input_focus,
73
77
  pack_setup,
74
78
  pack_unmap_window,
75
79
  unpack_client_message,
76
80
  unpack_error,
77
81
  unpack_event,
82
+ unpack_expose_event,
78
83
  unpack_get_atom_name,
79
84
  unpack_get_font_path,
80
85
  unpack_get_geometry,
@@ -155,6 +160,7 @@ class Drawable(Resource):
155
160
 
156
161
  async def draw_image(self, gc, x, y, w, h, depth, data, drawable=None):
157
162
  """Draw image."""
163
+ drawable = drawable or self
158
164
  max_size = self.display.info["max_request_length"]
159
165
  nb_pixels = w * h
160
166
  n = len(data)
@@ -194,8 +200,7 @@ class Drawable(Resource):
194
200
 
195
201
  async def put_text(self, gc, x, y, text, drawable=None):
196
202
  """Put text."""
197
- if drawable is None:
198
- drawable = self
203
+ drawable = drawable or self
199
204
  payload = pack_image_text(drawable, gc, x, y, text)
200
205
  self.display.connection.write(payload)
201
206
 
@@ -203,6 +208,11 @@ class Drawable(Resource):
203
208
  """Get geometry."""
204
209
  return await self.display.get_geometry(self)
205
210
 
211
+ async def create_pixmap(self, width, height, depth=None, drawable=None):
212
+ """Create a pixmap. *drawable* defaults to the root window."""
213
+ drawable = drawable or self
214
+ return await self.display.create_pixmap(width, height, depth, drawable)
215
+
206
216
 
207
217
  class ResourceManager:
208
218
  """Allocates resource IDs from the server base and mask."""
@@ -284,7 +294,7 @@ class Window(Drawable):
284
294
  delete_on_close=False,
285
295
  take_focus=False,
286
296
  **attrs,
287
- ):
297
+ ) -> Window:
288
298
  """Create window."""
289
299
  wid = self.display.resources.allocate()
290
300
  payload = pack_create_window(
@@ -482,7 +492,7 @@ class Screen:
482
492
  background_pixel=None,
483
493
  delete_on_close=True,
484
494
  **attrs,
485
- ):
495
+ ) -> Window:
486
496
  """Create window."""
487
497
  if parent is None:
488
498
  parent = self.root
@@ -538,7 +548,10 @@ class Display:
538
548
  self.commands = {cmd: cmd.name for cmd in Command}
539
549
  self.requests = asyncio.Queue()
540
550
  self.listeners = set()
541
- self._events = {EventType.ClientMessage: unpack_client_message}
551
+ self._events = {
552
+ EventType.ClientMessage: unpack_client_message,
553
+ EventType.Expose: unpack_expose_event,
554
+ }
542
555
  self._atom_cache = {}
543
556
  self._extensions_cache = None
544
557
  self._task = None
@@ -670,6 +683,16 @@ class Display:
670
683
  reply = await self.write_read(payload, unpack_get_font_path)
671
684
  return reply["path"]
672
685
 
686
+ async def set_font_path(self, paths=()):
687
+ """Set the font search path.
688
+
689
+ *paths* is a sequence of directory strings. Pass an empty sequence
690
+ (or call with no arguments) to restore the server default path.
691
+ This is a write-only request (no reply).
692
+ """
693
+ payload = pack_set_font_path(paths)
694
+ self.write(payload)
695
+
673
696
  async def get_atom(self, name, only_if_exists=False):
674
697
  """Intern *name* and cache the atom id."""
675
698
  if name in self._atom_cache:
@@ -759,9 +782,9 @@ class Display:
759
782
  self.write(payload)
760
783
  return fid
761
784
 
762
- async def open_font(self, **kwargs):
785
+ async def open_font(self, name=None, **kwargs):
763
786
  """Open font."""
764
- font = build_font(**kwargs)
787
+ font = build_font(**kwargs) if name is None else name
765
788
  return await self.open_font_name(font)
766
789
 
767
790
  async def tree(self, wnd):
@@ -811,6 +834,24 @@ class Display:
811
834
  payload = pack_clear_area(wnd, x, y, w, h, exposure)
812
835
  self.write(payload)
813
836
 
837
+ async def copy_area(self, src, dst, gc, src_x, src_y, width, height, dst_x=0, dst_y=0):
838
+ """Copy a rectangle from *src* to *dst*."""
839
+ self.write(pack_copy_area(src, dst, gc, src_x, src_y, width, height, dst_x, dst_y))
840
+
841
+ async def create_pixmap(self, width, height, depth=None, drawable=None):
842
+ """Create a pixmap. *drawable* defaults to the root window."""
843
+ if drawable is None:
844
+ drawable = self.default_screen().root
845
+ if depth is None:
846
+ depth = self.default_screen().root_depth
847
+ pid = self.resources.allocate()
848
+ self.write(pack_create_pixmap(pid, drawable, width, height, depth))
849
+ return pid
850
+
851
+ async def free_pixmap(self, pixmap):
852
+ """Free a pixmap."""
853
+ self.write(pack_free_pixmap(pixmap))
854
+
814
855
  async def set_input_focus(self, focus, revert_to=1, time=0):
815
856
  """Set keyboard input focus to *focus* (window id or resource)."""
816
857
  self.write(pack_set_input_focus(focus, revert_to=revert_to, time=time))
@@ -359,6 +359,36 @@ def unpack_list_properties(data, offset=0):
359
359
  return struct.unpack_from(f"{nb_atoms}I", data, offset), offset + nb_atoms * 4
360
360
 
361
361
 
362
+ def pack_list_strings(strings, encoding=DEFAULT_STRING_ENCODING) -> bytes:
363
+ """Pack a LISTofSTR (length-prefixed strings) with trailing pad to 4 bytes."""
364
+ parts = []
365
+ total = 0
366
+ for s in strings:
367
+ if isinstance(s, str):
368
+ b = s.encode(encoding)
369
+ else:
370
+ b = bytes(s)
371
+ if len(b) > 255:
372
+ raise ValueError(f"font path component longer than 255 bytes: {s!r}")
373
+ parts.append(struct.pack("B", len(b)) + b)
374
+ total += 1 + len(b)
375
+ body = b"".join(parts)
376
+ return body + b"\0" * pad(total)
377
+
378
+
379
+ def pack_set_font_path(paths=()) -> bytes:
380
+ """Pack a SetFontPath request.
381
+
382
+ *paths* is a sequence of directory strings (or bytes). An empty sequence
383
+ resets the font search path to the server default.
384
+ """
385
+ body = pack_list_strings(paths)
386
+ n = len(paths)
387
+ # header: opcode, unused, length, n, unused
388
+ length = 2 + len(body) // 4
389
+ return struct.pack("BxHH2x", Command.SetFontPath, length, n) + body
390
+
391
+
362
392
  def pack_get_font_path():
363
393
  """Pack a wire-protocol get font path request into ``bytes``."""
364
394
  return struct.pack("BxH", Command.GetFontPath, 1)
@@ -560,6 +590,15 @@ def unpack_setup_error(data, offset=0):
560
590
  return error, offset + length
561
591
 
562
592
 
593
+ def unpack_expose_event(data, offset=0):
594
+ fmt = "BxHIHHHHH"
595
+ fields = "raw_code", "sequence_number", "window", "x", "y", "width", "height", "count"
596
+ reply = struct.unpack_from(fmt, data, offset)
597
+ result = dict(zip(fields, reply, strict=True))
598
+ result["name"] = EventType.Expose.name
599
+ return result, offset + 32
600
+
601
+
563
602
  def unpack_event(event_map, data: bytes, offset: int = 0):
564
603
  """Unpack a event reply or event from *data* starting at *offset*."""
565
604
  raw_event_type = data[0]
@@ -641,6 +680,43 @@ def pack_clear_area(wnd, x, y, w, h, exposure=False):
641
680
  return struct.pack("BBHIhhHH", Command.ClearArea, int(exposure), 4, wnd, x, y, w, h)
642
681
 
643
682
 
683
+ def pack_copy_area(src, dst, gc, src_x, src_y, width, height, dst_x, dst_y):
684
+ """Pack CopyArea."""
685
+ return struct.pack(
686
+ "BxHIIIhhhhHH",
687
+ Command.CopyArea,
688
+ 7,
689
+ int(src),
690
+ int(dst),
691
+ int(gc),
692
+ int(src_x),
693
+ int(src_y),
694
+ int(dst_x),
695
+ int(dst_y),
696
+ int(width),
697
+ int(height),
698
+ )
699
+
700
+
701
+ def pack_create_pixmap(pid, drawable, width, height, depth):
702
+ """Pack CreatePixmap."""
703
+ return struct.pack(
704
+ "BBHIIHH",
705
+ Command.CreatePixmap,
706
+ int(depth),
707
+ 4,
708
+ int(pid),
709
+ int(drawable),
710
+ int(width),
711
+ int(height),
712
+ )
713
+
714
+
715
+ def pack_free_pixmap(pixmap):
716
+ """Pack FreePixmap."""
717
+ return struct.pack("BxHI", Command.FreePixmap, 2, int(pixmap))
718
+
719
+
644
720
  def pack_change_property(wid, property, property_type, format, data, mode):
645
721
  """Pack a wire-protocol change property request into ``bytes``."""
646
722
  if not isinstance(data, bytes | bytearray):