mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-15 05:12:28 +01:00
refactor(nix): a platform-neutral core in the VM library
Checks, Rect and the pixel helpers depend on no compositor. They move to komorebi_vm.core, which may not import komorebi_vm.kde, so that the package can go to the Windows port unchanged. komorebi_vm re-exports them and no suite changes. This is the first step of the cross-platform e2e plan.
This commit is contained in:
1 parent
9b90c508ee
commit
a37a9faf3d
11 files changed
+54
-40
No files matched your search
@@ -139,6 +139,10 @@ check also runs `ruff` and `ty` over it. The driver type checks every
|
||||
`test.py` with `ty` and `ruff` too, so a typo in a suite fails the build, not
|
||||
the run.
|
||||
|
||||
- The package `komorebi_vm.core` is what does not depend on the platform: `Checks`,
|
||||
`Rect` and the pixel helpers. It imports nothing of KDE, so that it can move to
|
||||
the Windows port unchanged. `komorebi_vm` re-exports them, so that a suite
|
||||
imports them from the same place as before.
|
||||
- `Checks` counts checks and fails once, at the end, so that one failure does
|
||||
not hide the ones after it. `finish()` prints `passed: N failed: M`, and its
|
||||
error names the checks that failed, which `nix build` shows in the last lines
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A suite imports what it needs from here:
|
||||
|
||||
* [`Checks`](komorebi_vm.checks.Checks) counts checks and fails at the end.
|
||||
* [`Checks`](komorebi_vm.core.checks.Checks) counts checks and fails at the end.
|
||||
* [`Kde`](komorebi_vm.kde.Kde) is a KWin session inside a test machine, with
|
||||
the tools to look at it (`kdotool`, `busctl`, `kscreen-doctor`, screenshots)
|
||||
and the komorebi daemon that runs in it.
|
||||
@@ -11,7 +11,7 @@ A suite imports what it needs from here:
|
||||
* [`Keys`](komorebi_vm.keys.Keys) presses keys on the machine's keyboard.
|
||||
* [`daemon`](komorebi_vm.daemon) reads what the daemon logs about the events it
|
||||
handles and the frames it asks for.
|
||||
* [`pixels`](komorebi_vm.pixels) reads a screenshot: the color of a pixel, and
|
||||
* [`pixels`](komorebi_vm.core.pixels) reads a screenshot: the color of a pixel, and
|
||||
the ring around a window.
|
||||
* [`latency`](komorebi_vm.latency) times the daemon's commands from a recording
|
||||
of the session bus.
|
||||
@@ -25,9 +25,11 @@ protocol, not on the test driver, so `nix/tests/vm/lib/tests` can run it
|
||||
against a fake machine (the `vm-lib` check does).
|
||||
"""
|
||||
|
||||
from komorebi_vm import daemon, latency, pixels, uinput
|
||||
from komorebi_vm.checks import Checks
|
||||
from komorebi_vm.kde import Kde, Outputs, Rect
|
||||
from komorebi_vm import daemon, latency, uinput
|
||||
from komorebi_vm.core import pixels
|
||||
from komorebi_vm.core.checks import Checks
|
||||
from komorebi_vm.core.geometry import Rect
|
||||
from komorebi_vm.kde import Kde, Outputs
|
||||
from komorebi_vm.keys import Keys
|
||||
from komorebi_vm.machine import Machine
|
||||
from komorebi_vm.pointer import Pointer
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
"""What does not depend on the platform: the vocabulary and the helpers a scenario is made of.
|
||||
|
||||
Nothing in this package may import from `komorebi_vm.kde` or know the name of a compositor,
|
||||
so that it can move to the Windows port unchanged.
|
||||
"""
|
||||
File renamed without changes.
@@ -0,0 +1,32 @@
|
||||
"""Rectangles, in the pixels of a screen."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Rect:
|
||||
"""A window's position and size, in pixels."""
|
||||
|
||||
x: int
|
||||
y: int
|
||||
w: int
|
||||
h: int
|
||||
|
||||
def overlaps(self, other: "Rect") -> bool:
|
||||
"""Returns true if and only if the two rectangles share at least one pixel."""
|
||||
return (
|
||||
self.x < other.x + other.w
|
||||
and other.x < self.x + self.w
|
||||
and self.y < other.y + other.h
|
||||
and other.y < self.y + self.h
|
||||
)
|
||||
|
||||
def inside(self, area: "Rect", slack: int = 0) -> bool:
|
||||
"""Returns true if and only if every pixel of this rectangle is a pixel of `area`, give
|
||||
or take `slack` pixels at each edge."""
|
||||
return (
|
||||
self.x >= area.x - slack
|
||||
and self.y >= area.y - slack
|
||||
and self.x + self.w <= area.x + area.w + slack
|
||||
and self.y + self.h <= area.y + area.h + slack
|
||||
)
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from komorebi_vm.kde import Rect
|
||||
from komorebi_vm.core.geometry import Rect
|
||||
|
||||
Color = tuple[int, int, int]
|
||||
|
||||
@@ -10,7 +10,7 @@ and its title, and every frame the daemon asks KWin for is a debug line.
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
|
||||
from komorebi_vm.kde import Rect
|
||||
from komorebi_vm.core.geometry import Rect
|
||||
|
||||
_EVENT = re.compile(r"processing event: (\w+) for window (\d+) with notification (\w+)")
|
||||
_ACTIVATED = re.compile(r"KWin activated window (\d+) \((.*)\)$", re.MULTILINE)
|
||||
|
||||
@@ -5,43 +5,14 @@ import re
|
||||
import shlex
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from komorebi_vm.core.geometry import Rect
|
||||
from komorebi_vm.machine import Machine
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Rect:
|
||||
"""A window's position and size, in pixels."""
|
||||
|
||||
x: int
|
||||
y: int
|
||||
w: int
|
||||
h: int
|
||||
|
||||
def overlaps(self, other: "Rect") -> bool:
|
||||
"""Returns true if and only if the two rectangles share at least one pixel."""
|
||||
return (
|
||||
self.x < other.x + other.w
|
||||
and other.x < self.x + self.w
|
||||
and self.y < other.y + other.h
|
||||
and other.y < self.y + self.h
|
||||
)
|
||||
|
||||
def inside(self, area: "Rect", slack: int = 0) -> bool:
|
||||
"""Returns true if and only if every pixel of this rectangle is a pixel of `area`, give
|
||||
or take `slack` pixels at each edge."""
|
||||
return (
|
||||
self.x >= area.x - slack
|
||||
and self.y >= area.y - slack
|
||||
and self.x + self.w <= area.x + area.w + slack
|
||||
and self.y + self.h <= area.y + area.h + slack
|
||||
)
|
||||
|
||||
|
||||
class Outputs:
|
||||
"""The outputs of a KWin session, as `kscreen-doctor` lists and changes them.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import re
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from komorebi_vm.kde import Rect
|
||||
from komorebi_vm.core.geometry import Rect
|
||||
|
||||
_HEADER = re.compile(r"^(method call|method return|signal|error) time=(\d+\.\d+) (.*)$")
|
||||
_STRING = ' string "'
|
||||
|
||||
@@ -10,7 +10,7 @@ requires-python = ">=3.12"
|
||||
dependencies = ["pillow"]
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["komorebi_vm"]
|
||||
packages = ["komorebi_vm", "komorebi_vm.core"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
komorebi_vm = ["py.typed"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
from komorebi_vm import Rect
|
||||
from komorebi_vm.pixels import leftmost, near, pixel, ring
|
||||
from komorebi_vm.core.pixels import leftmost, near, pixel, ring
|
||||
|
||||
BLUE = (66, 165, 245)
|
||||
GRAY = (128, 128, 128)
|
||||
|
||||
Reference in new issue
Block a user