Files
asepharyana 1cdb82a76f Sync config from arch
- hypr/apps.lua
- hypr/autostart.lua
- hypr/envs.lua
- hypr/hyprland.lua
- hypr/hyprsunset.conf
- hypr/input.lua
- hypr/looknfeel.lua
- hypr/omasettings.lua
- hypr/xdph.conf
- omarchy/branding/about.txt
- omarchy/branding/screensaver.txt
- omarchy/extensions/omarchy-menu.jsonc
- omarchy/hooks/battery-low.d/play-warning-sound.sample
- omarchy/hooks/font-set.d/show-font-notification.sample
- omarchy/hooks/post-boot.d/weather.sample
- omarchy/hooks/post-update.d/install-voxtype.hook
- omarchy/hooks/post-update.d/setup-agent.hook
- omarchy/hooks/post-update.d/setup-fingerprint.hook
- omarchy/hooks/post-update.d/show-update-notification.sample
- omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
- omarchy/hooks/theme-set.d/show-theme-notification.sample
- omarchy/shell.json
- omarchy/shell.toml
- omarchy/theme.name
- omarchy/themes/azure-glow/README.md
- omarchy/themes/azure-glow/alacritty.toml
- omarchy/themes/azure-glow/btop.theme
- omarchy/themes/azure-glow/hyprland.conf
- omarchy/themes/azure-glow/hyprlock.conf
- omarchy/themes/azure-glow/icons.theme
- … 269 more
2026-09-23 15:19:12 +07:00

127 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Locate the plugin panel in a screenshot by its accent border.
Trimming to "any accent-coloured pixel" is not enough: the compositor draws the
focused window's border in the same accent, so a trim swallows whatever sits
behind the panel. The panel is instead found as a filled rectangle -- four
borders enclosing a region -- and the largest such rectangle is returned.
The accent itself is read out of the image rather than hardcoded. It is a theme
colour, so a hardcoded value silently stops matching the day the operator
changes themes -- which is exactly how this last failed, with every shot
reported as "could not locate the panel border" and no hint as to why.
find_panel.py <image> [--accent RRGGBB] -> "WxH+X+Y" on stdout
"""
import sys
from collections import Counter
from PIL import Image
# Enough slack for antialiasing and the border's own gradient, not enough to
# merge two distinct theme colours.
TOLERANCE = 26
# The panel is wider than any window border is thick.
MIN_RUN = 260
def close(px, target, tol=TOLERANCE):
return all(abs(px[i] - target[i]) <= tol for i in range(3))
def candidate_accents(img, limit=6):
"""Saturated colours in the image, most common first.
The panel border is a solid run of one theme colour, so it is always among
the most common saturated pixels. Returning several candidates means a
highlighted row or a colourful wallpaper cannot derail the search.
"""
w, h = img.size
px = img.load()
counts = Counter()
for y in range(0, h, 2):
for x in range(0, w, 2):
r, g, b = px[x, y]
if max(r, g, b) > 110 and (max(r, g, b) - min(r, g, b)) > 60:
counts[(r, g, b)] += 1
accents = []
for colour, _ in counts.most_common():
# Skip anything already covered by a candidate we kept.
if any(close(colour, kept) for kept in accents):
continue
accents.append(colour)
if len(accents) >= limit:
break
return accents
def find_box(img, accent):
w, h = img.size
px = img.load()
# Rows that contain a long horizontal run of accent pixels are candidate
# top/bottom borders.
runs = {} # row -> (start, end) of its longest accent run
for y in range(h):
best = (0, 0, 0)
run_start, run_len = None, 0
for x in range(w):
if close(px[x, y], accent):
if run_start is None:
run_start = x
run_len += 1
else:
if run_len > best[0]:
best = (run_len, run_start, x - 1)
run_start, run_len = None, 0
if run_len > best[0]:
best = (run_len, run_start, w - 1)
if best[0] >= MIN_RUN:
runs[y] = (best[1], best[2])
if not runs:
return None
# Pair each top edge with the furthest bottom edge sharing its extent, and
# keep the tallest rectangle: that is the panel, not a window border.
best_box = None
ys = sorted(runs)
for i, top in enumerate(ys):
x0, x1 = runs[top]
for bottom in reversed(ys[i + 1:]):
bx0, bx1 = runs[bottom]
if abs(bx0 - x0) <= 4 and abs(bx1 - x1) <= 4 and bottom - top > 120:
box = (x1 - x0 + 1, bottom - top + 1, x0, top)
if best_box is None or box[0] * box[1] > best_box[0] * best_box[1]:
best_box = box
break
return best_box
def main():
path = sys.argv[1]
img = Image.open(path).convert("RGB")
if "--accent" in sys.argv:
h = sys.argv[sys.argv.index("--accent") + 1].lstrip("#")
accents = [tuple(int(h[i:i+2], 16) for i in (0, 2, 4))]
else:
accents = candidate_accents(img)
if not accents:
sys.exit("no saturated colour in the image to use as an accent")
for accent in accents:
box = find_box(img, accent)
if box:
print("%dx%d+%d+%d" % box)
return
tried = ", ".join("#%02X%02X%02X" % a for a in accents)
sys.exit("no enclosed rectangle found (tried %s)" % tried)
if __name__ == "__main__":
main()