Three things in this window that a container cannot answer, and they are now the top of the next-steps list: a real key pressed into Parent Call, a television switched on after the computer, and a disc that plays. Also brings the comments in the shared libraries up to date. The only mention of sway left in the tree is the upgrade step that removes the sway-era login shell from a room being upgraded. Co-Authored-By: Claude Opus 5 <[email protected]>
97 lines
2.9 KiB
Python
97 lines
2.9 KiB
Python
"""Icons that stay sharp when the compositor scales the session.
|
|
|
|
new_from_file_at_scale rasterises an SVG at its *logical* size. Under
|
|
output scaling the compositor then enlarges that finished raster,
|
|
which is why icons went soft while the clock text and the dock surface
|
|
stayed crisp: those are drawn by GTK at the display's real pixel
|
|
density, and the icon was not.
|
|
|
|
GTK 3 has no fractional scaling, so the compositor advertises the ceiling
|
|
integer
|
|
scale and scales the finished buffer down. Measured: scale
|
|
factor 1 at output scale 1.0, and 2 at 1.25, 1.5 and 2.0.
|
|
|
|
So the fix is to rasterise at size * scale_factor physical pixels and
|
|
hand GTK a cairo surface carrying that device scale. The icon lands at
|
|
the real density, while its logical size — and therefore the whole dock
|
|
layout — is exactly what it was before.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "3.0")
|
|
gi.require_version("Gdk", "3.0")
|
|
gi.require_version("GdkPixbuf", "2.0")
|
|
|
|
from gi.repository import Gdk, GdkPixbuf, GLib, Gtk # noqa: E402
|
|
|
|
ICON_DIR = Path("/etc/ekklesia-classroom/icons")
|
|
|
|
|
|
def render_surface(path, size, scale, window=None):
|
|
"""An SVG at `size` logical pixels, drawn at `scale` times the density.
|
|
|
|
The box semantics match the old custom_icon(): the artwork is fitted
|
|
inside a square of the requested size with its aspect preserved, so
|
|
a wide logo like the DVD wordmark keeps the proportions it has now.
|
|
"""
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
|
str(path),
|
|
size * scale,
|
|
size * scale,
|
|
True,
|
|
)
|
|
|
|
return Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, window)
|
|
|
|
|
|
class ScalableIcon(Gtk.Image):
|
|
"""A custom SVG icon, redrawn whenever the display density changes."""
|
|
|
|
def __init__(self, filename, size, directory=None):
|
|
super().__init__()
|
|
|
|
self.directory = directory or ICON_DIR
|
|
self.size = size
|
|
self.drawn_at = None
|
|
self.usable = False
|
|
|
|
self.set_icon(filename)
|
|
|
|
# The scale factor is not known until the widget is on a display,
|
|
# and it changes if UI_SCALE is changed under a running session.
|
|
self.connect("realize", lambda *_: self.redraw())
|
|
self.connect("notify::scale-factor", lambda *_: self.redraw())
|
|
|
|
def set_icon(self, filename):
|
|
self.path = self.directory / filename
|
|
self.usable = self.path.exists()
|
|
self.drawn_at = None
|
|
|
|
self.redraw()
|
|
|
|
def redraw(self):
|
|
if not self.usable:
|
|
return
|
|
|
|
scale = self.get_scale_factor() or 1
|
|
|
|
if scale == self.drawn_at:
|
|
return
|
|
|
|
try:
|
|
surface = render_surface(
|
|
self.path,
|
|
self.size,
|
|
scale,
|
|
self.get_window(),
|
|
)
|
|
except (GLib.Error, MemoryError):
|
|
self.usable = False
|
|
return
|
|
|
|
self.set_from_surface(surface)
|
|
self.drawn_at = scale
|