82 lines
4.0 KiB
Python
82 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""L0: как обычный mpv ведёт себя плавающим окном в Cinnamon (эталон для L1/L8).
|
|
|
|
float_test.py <037|041>
|
|
Запускает mpv --ontop --no-border --geometry=640x360-24-24 и по IPC переключает режимы,
|
|
после каждого шага печатает геометрию окна, _NET_WM_STATE, рамку (_NET_FRAME_EXTENTS) и активное окно.
|
|
"""
|
|
import json, os, socket, subprocess, sys, time
|
|
from Xlib import X, display
|
|
|
|
# mpv 0.37 из репозитория Mint (или распакованный из .deb без установки: apt download mpv; dpkg -x)
|
|
M037 = os.environ.get("MPV037", "mpv")
|
|
RUN = os.path.join(os.environ.get("XDG_RUNTIME_DIR", "/run/user/1000"), "l0")
|
|
os.makedirs(RUN, exist_ok=True)
|
|
ver = sys.argv[1]
|
|
sock = os.path.join(RUN, f"float{ver}.sock")
|
|
if os.path.exists(sock): os.unlink(sock)
|
|
item = "00000000000000000000000000000000"
|
|
url = f"http://192.168.1.10:8096/Videos/{item}/stream?static=true&mediaSourceId={item}"
|
|
opts = ["--config=no", "--mute=yes", "--start=30%", "--hwdec=auto-safe", "--vo=gpu", "--gpu-api=opengl",
|
|
"--ontop", "--no-border", "--geometry=640x360-24-24", "--screen=0", "--keep-open=yes",
|
|
"--title=L0 float", f"--input-ipc-server={sock}"]
|
|
cmd = ([M037] if ver == "037" else ["flatpak", "run", "--filesystem=xdg-run/l0", "io.mpv.Mpv"]) + opts + [url]
|
|
|
|
d = display.Display(); root = d.screen().root
|
|
A = lambda n: d.intern_atom(n)
|
|
NET_ACTIVE, NET_STATE, FRAME, NAME = A("_NET_ACTIVE_WINDOW"), A("_NET_WM_STATE"), A("_NET_FRAME_EXTENTS"), A("_NET_WM_NAME")
|
|
|
|
def active():
|
|
p = root.get_full_property(NET_ACTIVE, X.AnyPropertyType)
|
|
return p.value[0] if p else None
|
|
|
|
def find_win():
|
|
for _ in range(100):
|
|
p = root.get_full_property(A("_NET_CLIENT_LIST"), X.AnyPropertyType)
|
|
for wid in (p.value if p else []):
|
|
w = d.create_resource_object("window", wid)
|
|
n = w.get_full_property(NAME, 0)
|
|
if n and b"L0 float" in (n.value if isinstance(n.value, bytes) else n.value.encode()): return w
|
|
time.sleep(0.1)
|
|
|
|
def geom(w):
|
|
g = w.get_geometry(); t = w.translate_coords(root, 0, 0)
|
|
st = w.get_full_property(NET_STATE, X.AnyPropertyType)
|
|
states = [d.get_atom_name(x).replace("_NET_WM_STATE_", "") for x in (st.value if st else [])]
|
|
fe = w.get_full_property(FRAME, X.AnyPropertyType)
|
|
return {"x": -t.x, "y": -t.y, "w": g.width, "h": g.height, "state": states,
|
|
"frame": list(fe.value) if fe else None, "focused": active() == w.id}
|
|
|
|
class Ipc:
|
|
def __init__(self):
|
|
for _ in range(200):
|
|
try: self.s = socket.socket(socket.AF_UNIX); self.s.connect(sock); break
|
|
except OSError: time.sleep(0.05)
|
|
self.f = self.s.makefile("rb")
|
|
def cmd(self, *a):
|
|
self.s.sendall(json.dumps({"command": list(a), "request_id": 1}).encode() + b"\n")
|
|
while True:
|
|
m = json.loads(self.f.readline())
|
|
if m.get("request_id") == 1: return m
|
|
|
|
before = active()
|
|
proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
ipc = Ipc(); w = find_win(); time.sleep(1.5)
|
|
print("active before launch:", hex(before) if before else None)
|
|
steps = [("start (ontop, no border)", None),
|
|
("ontop=no", ("set", "ontop", "no")),
|
|
("border=yes", ("set", "border", "yes")),
|
|
("fullscreen=yes", ("set", "fullscreen", "yes")),
|
|
("fullscreen=no", ("set", "fullscreen", "no")),
|
|
("border=no + ontop=yes", ("set", "border", "no")),
|
|
("screen=1 portrait (geometry 640x360-24-24)", None),
|
|
("resize 960x540 via geometry", ("set", "geometry", "960x540-24-24"))]
|
|
for label, c in steps:
|
|
if c: ipc.cmd(*c)
|
|
if label.startswith("border=no"): ipc.cmd("set", "ontop", "yes")
|
|
if label.startswith("screen=1"):
|
|
ipc.cmd("set", "screen", "1"); ipc.cmd("set", "geometry", "640x360-24-24")
|
|
time.sleep(1.2)
|
|
print(f"{label:45s}", json.dumps(geom(w)))
|
|
ipc.cmd("quit"); proc.wait(timeout=10)
|