mirror of
https://git.srhtnin.garden/sarah/SaraSuite.git
synced 2026-10-13 18:18:12 +02:00
Per-program Flet client app IDs so the dock shows the right icons
This commit is contained in:
1 parent
19bbad33a4
commit
56f10885de
12 files changed
+114
-18
No files matched your search
@@ -1,6 +1,7 @@
|
||||
"""SaraSuite entry point: installs, updates and removes Sara programs.
|
||||
With arguments it runs the command-line mode instead (see Cli.py)."""
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
@@ -11,7 +12,7 @@ import flet as ft
|
||||
|
||||
from Core import (
|
||||
AppName, ApplyAppearance, AssetsDir, BuildTheme, BundledFontFamily, ChipButtonStyle, DataDir,
|
||||
DataHome, EffectiveSettings, LoadConfig, Palette,
|
||||
DataHome, EffectiveSettings, LoadConfig, ManifestsDir, Palette,
|
||||
)
|
||||
from Dialogs import ShowInstallDialog, ShowOptionsDialog, ShowRemoveDialog, ShowUpdateDialog
|
||||
from Installer import Apply, Fetch, FetchCommit, GetStatus, PlanConfigs, PlanRemoval, Remove, SourceVersion
|
||||
@@ -27,6 +28,15 @@ _MenuEntry = DataHome / "applications" / "SaraSuite.desktop"
|
||||
_MenuIcon = DataDir / "SaraSuite.png"
|
||||
|
||||
|
||||
def _LoadFletIdentity():
|
||||
# Lives in Programs/ (bundled as data) because installed programs get a
|
||||
# copy of the same file next to them.
|
||||
Spec = importlib.util.spec_from_file_location("FletIdentity", ManifestsDir / "FletIdentity.py")
|
||||
Module = importlib.util.module_from_spec(Spec)
|
||||
Spec.loader.exec_module(Module)
|
||||
return Module
|
||||
|
||||
|
||||
def _SetMenuEntry(Enabled: bool) -> None:
|
||||
if not Enabled:
|
||||
_MenuEntry.unlink(missing_ok=True)
|
||||
@@ -39,7 +49,7 @@ def _SetMenuEntry(Enabled: bool) -> None:
|
||||
"[Desktop Entry]\nType=Application\nName=SaraSuite\n"
|
||||
"Comment=Install, update and remove Sara programs\n"
|
||||
f"Exec={_AppImage}\nIcon={_MenuIcon}\nTerminal=false\n"
|
||||
f"Categories=System;\nStartupNotify=true\nStartupWMClass={AppName}\n"
|
||||
f"Categories=System;\nStartupNotify=true\nStartupWMClass=srhtnin.{AppName}\n"
|
||||
)
|
||||
|
||||
|
||||
@@ -234,7 +244,8 @@ if __name__ == "__main__":
|
||||
from Cli import Run
|
||||
|
||||
sys.exit(Run(sys.argv[1:]))
|
||||
# The window's app ID, so the desktop matches it to SaraSuite.desktop
|
||||
# (icon, name) instead of showing it as Flet's generic "com.appveyor.flet".
|
||||
os.environ.setdefault("FLET_APP_ID", AppName)
|
||||
# A Flet client copy with the window app ID srhtnin.SaraSuite, so the
|
||||
# desktop matches the window to SaraSuite.desktop (StartupWMClass) instead
|
||||
# of showing it as "com.appveyor.flet". See Programs/FletIdentity.py.
|
||||
os.environ.setdefault("FLET_VIEW_PATH", _LoadFletIdentity().ClientFor(f"srhtnin.{AppName}"))
|
||||
ft.run(Main, assets_dir=str(AssetsDir))
|
||||
@@ -30,7 +30,7 @@ Exec=SaraSuite
|
||||
Icon=SaraSuite
|
||||
Terminal=false
|
||||
Categories=System;
|
||||
StartupWMClass=SaraSuite
|
||||
StartupWMClass=srhtnin.SaraSuite
|
||||
DESKTOP
|
||||
|
||||
mkdir -p dist
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"""Give a Flet program's window its own app ID on Linux, so the desktop can
|
||||
match it to the program's .desktop entry (icon, name) instead of showing
|
||||
every Flet program as "com.appveyor.flet" with a generic icon.
|
||||
|
||||
The Flet client's ID is a string built into its small launcher binary
|
||||
(`flet`, ~24 KB); the client sets it as both its GTK application ID and its
|
||||
process name, so Flet's own FLET_APP_ID (which only changes argv[0]) can't
|
||||
override it, on Wayland or X11. Instead this makes a per-program copy of the
|
||||
client in ~/.cache/SaraSuite/FletClient/ with that string replaced; the big
|
||||
parts (lib/, data/, share/) are symlinks to the original, so a copy costs
|
||||
~24 KB. Pointing FLET_VIEW_PATH at it makes Flet use the copy.
|
||||
|
||||
Used two ways:
|
||||
- SaraSuite imports it and calls ClientFor() before ft.run.
|
||||
- Installed programs' launchers run it with their own venv's python:
|
||||
export FLET_VIEW_PATH="$(python FletIdentity.py srhtnin.SaraFem)"
|
||||
It prints the copy's folder, or nothing if anything goes wrong, in which
|
||||
case Flet just uses its normal client.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
OriginalId = b"com.appveyor.flet"
|
||||
CopiesDir = Path.home() / ".cache" / "SaraSuite" / "FletClient"
|
||||
|
||||
|
||||
def ClientFor(AppId: str) -> str:
|
||||
"""Folder of a client copy whose app ID is AppId ("" if not possible).
|
||||
AppId needs a dot (a GTK rule) and at most 17 characters, the length of
|
||||
the original it replaces in place."""
|
||||
try:
|
||||
Encoded = AppId.encode("ascii")
|
||||
if "." not in AppId or not 0 < len(Encoded) <= len(OriginalId):
|
||||
return ""
|
||||
from flet_desktop import ensure_client_cached # the program's own Flet version
|
||||
|
||||
Original = Path(ensure_client_cached()) / "flet"
|
||||
Binary = (Original / "flet").read_bytes()
|
||||
Copy = CopiesDir / f"{Original.parent.name}-{AppId}" / "flet"
|
||||
Patched = Binary.replace(
|
||||
b"\x00" + OriginalId + b"\x00",
|
||||
b"\x00" + Encoded.ljust(len(OriginalId), b"\x00") + b"\x00",
|
||||
)
|
||||
if Binary.count(b"\x00" + OriginalId + b"\x00") != 1:
|
||||
return "" # not the client layout this was written for
|
||||
|
||||
Target = Copy / "flet"
|
||||
if not (Target.exists() and Target.read_bytes() == Patched):
|
||||
Copy.mkdir(parents=True, exist_ok=True)
|
||||
for Part in ("lib", "data", "share"):
|
||||
Link = Copy / Part
|
||||
if Link.is_symlink() or Link.exists():
|
||||
Link.unlink()
|
||||
if (Original / Part).exists():
|
||||
Link.symlink_to(Original / Part)
|
||||
Temporary = Target.with_suffix(".new")
|
||||
Temporary.write_bytes(Patched)
|
||||
Temporary.chmod(0o755)
|
||||
Temporary.replace(Target)
|
||||
return str(Copy)
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(ClientFor(sys.argv[1]) if len(sys.argv) > 1 else "")
|
||||
@@ -65,3 +65,8 @@ Note = "thumbnail cache"
|
||||
Path = "{ConfigHome}/SaraFem"
|
||||
Kind = "Config"
|
||||
Note = "old pre-SaraSuite config location"
|
||||
|
||||
# Shared with the other Flet programs; see Programs/FletIdentity.py.
|
||||
[[Templates]]
|
||||
Source = "../../FletIdentity.py"
|
||||
Target = "{ProgramDir}/FletIdentity.py"
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/bin/sh
|
||||
# Written by SaraSuite. Runs from the app folder so Flet finds assets/.
|
||||
# The window's app ID, so the desktop matches it to {Name}.desktop (icon, name)
|
||||
# instead of showing every Flet program as "com.appveyor.flet".
|
||||
export FLET_APP_ID="{Name}"
|
||||
# A Flet client copy with the window app ID srhtnin.{Name}, so the desktop
|
||||
# matches the window to {Name}.desktop (StartupWMClass) instead of showing it
|
||||
# as "com.appveyor.flet". Empty if that fails: Flet then uses its own client.
|
||||
export FLET_VIEW_PATH="$("{Python}" "{ProgramDir}/FletIdentity.py" "srhtnin.{Name}")"
|
||||
cd "{AppDir}" || exit 1
|
||||
exec "{Python}" Main.py "$@"
|
||||
@@ -8,4 +8,4 @@ Terminal=false
|
||||
Categories=System;FileManager;
|
||||
MimeType=inode/directory;
|
||||
StartupNotify=true
|
||||
StartupWMClass={Name}
|
||||
StartupWMClass=srhtnin.{Name}
|
||||
@@ -25,3 +25,8 @@ Target = "{ApplicationsDir}/SaraSort.desktop"
|
||||
Path = "{ConfigDir}"
|
||||
Kind = "Config"
|
||||
Note = "SaraSort's settings, flowchart layout and your templates"
|
||||
|
||||
# Shared with the other Flet programs; see Programs/FletIdentity.py.
|
||||
[[Templates]]
|
||||
Source = "../../FletIdentity.py"
|
||||
Target = "{ProgramDir}/FletIdentity.py"
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/bin/sh
|
||||
# Written by SaraSuite. Runs from the app folder so Flet finds assets/.
|
||||
# The window's app ID, so the desktop matches it to {Name}.desktop (icon, name)
|
||||
# instead of showing every Flet program as "com.appveyor.flet".
|
||||
export FLET_APP_ID="{Name}"
|
||||
# A Flet client copy with the window app ID srhtnin.{Name}, so the desktop
|
||||
# matches the window to {Name}.desktop (StartupWMClass) instead of showing it
|
||||
# as "com.appveyor.flet". Empty if that fails: Flet then uses its own client.
|
||||
export FLET_VIEW_PATH="$("{Python}" "{ProgramDir}/FletIdentity.py" "srhtnin.{Name}")"
|
||||
cd "{AppDir}" || exit 1
|
||||
exec "{Python}" Main.py "$@"
|
||||
@@ -7,4 +7,4 @@ Icon={AppDir}/assets/Icon.png
|
||||
Terminal=false
|
||||
Categories=Utility;
|
||||
StartupNotify=true
|
||||
StartupWMClass={Name}
|
||||
StartupWMClass=srhtnin.{Name}
|
||||
@@ -35,3 +35,8 @@ Target = "{ApplicationsDir}/SaraTag.desktop"
|
||||
Path = "{ConfigDir}"
|
||||
Kind = "Config"
|
||||
Note = "SaraTag's settings, Records, Libraries, history and Lexicons (Books next to your files are never touched)"
|
||||
|
||||
# Shared with the other Flet programs; see Programs/FletIdentity.py.
|
||||
[[Templates]]
|
||||
Source = "../../FletIdentity.py"
|
||||
Target = "{ProgramDir}/FletIdentity.py"
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/bin/sh
|
||||
# Written by SaraSuite. Runs from the app folder so Flet finds assets/.
|
||||
# The window's app ID, so the desktop matches it to {Name}.desktop (icon, name)
|
||||
# instead of showing every Flet program as "com.appveyor.flet".
|
||||
export FLET_APP_ID="{Name}"
|
||||
# A Flet client copy with the window app ID srhtnin.{Name}, so the desktop
|
||||
# matches the window to {Name}.desktop (StartupWMClass) instead of showing it
|
||||
# as "com.appveyor.flet". Empty if that fails: Flet then uses its own client.
|
||||
export FLET_VIEW_PATH="$("{Python}" "{ProgramDir}/FletIdentity.py" "srhtnin.{Name}")"
|
||||
cd "{AppDir}" || exit 1
|
||||
exec "{Python}" Main.py "$@"
|
||||
@@ -7,4 +7,4 @@ Icon={AppDir}/assets/Icon.png
|
||||
Terminal=false
|
||||
Categories=Utility;Graphics;
|
||||
StartupNotify=true
|
||||
StartupWMClass={Name}
|
||||
StartupWMClass=srhtnin.{Name}
|
||||
Reference in new issue
Block a user