2 Commits
Author SHA1 Message Date
oxpoe 76790e0321 feat(autostart): tray ikut autostart saat login, tersembunyi (--tray-only)
- checkbox 'Mulai otomatis saat login' kini mengurus layanan (sender) DAN entri autostart tray
- entri autostart milik tool integrasi AppImage yang menunjuk AppImage sama ditandai Hidden=true
  supaya saat login hanya satu instance tray yang jalan, tanpa jendela
- install.sh ikut memasang entri autostart tray + menghapusnya saat --uninstall
- sender: retry 1 detik dan pesan galat izin tidak diulang-ulang (ACL udev baru aktif
  beberapa detik setelah login) — sebelumnya ~12 detik penuh galat beruntun saat boot
- versi 1.3.3
2026-10-13 10:27:05 +07:00
oxpoe c3d34a2788 fix(packaging): nama entri menu di dalam AppImage jadi 'IceBurg LCD'
AppImage manager menyalin .desktop dari dalam bundel, sehingga entri di app drawer
masih bernama merek vendor walau UI aplikasinya sudah diganti. Kini bundel membawa
Name=IceBurg LCD (GenericName/Comment juga tanpa merek). Versi 1.3.2
2026-10-13 09:51:06 +07:00
5 changed files with 92 additions and 10 deletions

No files matched your search

+4 -1
View File
@@ -101,7 +101,10 @@ temperature source (Tctl vs Tccd).
send/sample interval — saved to `~/.config/gamemax-report/config.json` send/sample interval — saved to `~/.config/gamemax-report/config.json`
- **Clear mode indicators**: green "showing sensor data" / orange "blank (zeros)" / - **Clear mode indicators**: green "showing sensor data" / orange "blank (zeros)" /
grey "sending stopped", dimmed tray icon and a desktop notification per change grey "sending stopped", dimmed tray icon and a desktop notification per change
- **Autostart** via a systemd user unit (equivalent of the vendor's Task Scheduler trick) - **Autostart** at login: a systemd user unit for the sender **and** an XDG autostart entry that
starts the tray app with `--tray-only` (no window pops up; open it from the tray menu). The
*Mulai otomatis saat login* checkbox manages both, and disables any other autostart entry that
would launch the same AppImage with a window.
- **Plug and play**: the service waits for the device, streams while present and keeps - **Plug and play**: the service waits for the device, streams while present and keeps
waiting through unplug/replug; a udev rule grants access to the logged-in user waiting through unplug/replug; a udev rule grants access to the logged-in user
- **Single instance**: launching the app again just raises the running window - **Single instance**: launching the app again just raises the running window
+14 -1
View File
@@ -54,6 +54,7 @@ if [ "$UNINSTALL" = 1 ]; then
systemctl --user reset-failed "$SERVICE" 2>/dev/null || true; } systemctl --user reset-failed "$SERVICE" 2>/dev/null || true; }
rm -rf "$OPT_DIR" "$DATA_DIR" rm -rf "$OPT_DIR" "$DATA_DIR"
rm -f "$DESKTOP_DIR/gamemax-lcd.desktop" "$ICON_DIR/gamemax-lcd.png" rm -f "$DESKTOP_DIR/gamemax-lcd.desktop" "$ICON_DIR/gamemax-lcd.png"
rm -f "$HOME/.config/autostart/iceburg-lcd.desktop"
[ "$REMOVE_DATA" = 1 ] && rm -rf "$HOME/.config/gamemax-report" [ "$REMOVE_DATA" = 1 ] && rm -rf "$HOME/.config/gamemax-report"
command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
if [ -f "$RULE" ]; then if [ -f "$RULE" ]; then
@@ -135,7 +136,7 @@ else
say " dilewati (--no-service)" say " dilewati (--no-service)"
fi fi
say "5/6 memasang entri menu + ikon …" say "5/6 memasang entri menu, ikon, dan autostart tray …"
cat > "$DESKTOP_DIR/gamemax-lcd.desktop" <<EOF cat > "$DESKTOP_DIR/gamemax-lcd.desktop" <<EOF
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
@@ -150,6 +151,18 @@ EOF
command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
command -v gtk-update-icon-cache >/dev/null 2>&1 && \ command -v gtk-update-icon-cache >/dev/null 2>&1 && \
gtk-update-icon-cache -f -t "$HOME/.local/share/icons/hicolor" 2>/dev/null || true gtk-update-icon-cache -f -t "$HOME/.local/share/icons/hicolor" 2>/dev/null || true
mkdir -p "$HOME/.config/autostart"
cat > "$HOME/.config/autostart/iceburg-lcd.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=IceBurg LCD (tray)
Comment=Layar LCD AIO — berjalan di tray saat login (tanpa jendela)
Exec="$APP" --tray-only
Icon=iceburg-lcd
Terminal=false
X-GNOME-Autostart-enabled=true
EOF
say " autostart tray: ~/.config/autostart/iceburg-lcd.desktop (--tray-only)"
say "6/6 aturan udev (izin device) …" say "6/6 aturan udev (izin device) …"
RULE_TMP="$(mktemp)" RULE_TMP="$(mktemp)"
+3 -3
View File
@@ -42,9 +42,9 @@ cp "$ROOT_DIR/src/icon.png" "$APPDIR/gamemax-lcd.png"
cat > "$APPDIR/gamemax-lcd.desktop" <<'EOF' cat > "$APPDIR/gamemax-lcd.desktop" <<'EOF'
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Name=GAMEMAX IceBurg LCD Name=IceBurg LCD
GenericName=AI LCD controller GenericName=Kontrol LCD AIO
Comment=Status, on/off, monitor, and settings for the GAMEMAX IceBurg AIO LCD Comment=Status, nyala/mati, pemantauan, dan pengaturan LCD AIO IceBurg Digital
Exec=gamemax-lcd Exec=gamemax-lcd
Icon=gamemax-lcd Icon=gamemax-lcd
Terminal=false Terminal=false
+64 -2
View File
@@ -8,6 +8,7 @@ mengendalikannya dan memakai gamemax_report.py sebagai pengirim.
gamemax_gui.py --tray-only # tanpa jendela gamemax_gui.py --tray-only # tanpa jendela
gamemax_gui.py --selftest # uji muat tanpa tampilan (untuk build AppImage) gamemax_gui.py --selftest # uji muat tanpa tampilan (untuk build AppImage)
""" """
import glob
import hashlib import hashlib
import json import json
import math import math
@@ -32,7 +33,7 @@ from PySide6.QtWidgets import (
) )
from PySide6.QtNetwork import QLocalServer, QLocalSocket from PySide6.QtNetwork import QLocalServer, QLocalSocket
APP_VERSION = "1.3.1" APP_VERSION = "1.3.3"
APP_NAME = "IceBurg LCD" APP_NAME = "IceBurg LCD"
REPO = "oxpoe/Gamemax-linux-apps" REPO = "oxpoe/Gamemax-linux-apps"
RELEASES_API = f"https://api.github.com/repos/{REPO}/releases/latest" RELEASES_API = f"https://api.github.com/repos/{REPO}/releases/latest"
@@ -49,6 +50,8 @@ CONFIG_PATH = gr.CONFIG_PATH
INSTALL_DIR = os.path.expanduser("~/.local/share/gamemax-report") INSTALL_DIR = os.path.expanduser("~/.local/share/gamemax-report")
UNIT_PATH = os.path.expanduser(f"~/.config/systemd/user/{SERVICE}") UNIT_PATH = os.path.expanduser(f"~/.config/systemd/user/{SERVICE}")
RULE_PATH = "/etc/udev/rules.d/70-gamemax.rules" RULE_PATH = "/etc/udev/rules.d/70-gamemax.rules"
AUTOSTART_DIR = os.path.expanduser("~/.config/autostart")
AUTOSTART_FILE = os.path.join(AUTOSTART_DIR, "iceburg-lcd.desktop")
UNIT_TEMPLATE = """[Unit] UNIT_TEMPLATE = """[Unit]
Description=IceBurg LCD — HID sensor display bridge (Linux) Description=IceBurg LCD — HID sensor display bridge (Linux)
@@ -163,6 +166,63 @@ def open_link(url, parent=None):
parent.log(f"tidak bisa membuka tautan: {url}") parent.log(f"tidak bisa membuka tautan: {url}")
def other_autostart_entries(appimage):
"""Entri autostart lain yang menjalankan AppImage yang sama (mis. buatan tool integrasi)."""
found = []
for path in glob.glob(os.path.join(AUTOSTART_DIR, "*.desktop")):
if path == AUTOSTART_FILE:
continue
try:
text = open(path).read()
except OSError:
continue
if appimage and appimage in text:
found.append(path)
return found
def set_tray_autostart(enable, appimage):
"""Autostart ikon tray saat login, tersembunyi (tanpa jendela)."""
if not appimage:
return "tidak berjalan dari AppImage — autostart tray dilewati"
os.makedirs(AUTOSTART_DIR, exist_ok=True)
notes = []
if enable:
with open(AUTOSTART_FILE, "w") as fh:
fh.write(f"""[Desktop Entry]
Type=Application
Name={APP_NAME} (tray)
Comment=Layar LCD AIO — berjalan di tray saat login (tanpa jendela)
Exec="{appimage}" --tray-only
Icon=iceburg-lcd
Terminal=false
X-GNOME-Autostart-enabled=true
""")
notes.append("autostart tray dibuat (--tray-only, tanpa jendela)")
for path in other_autostart_entries(appimage):
try:
text = open(path).read()
if "Hidden=true" not in text:
with open(path, "a") as fh:
fh.write("\nHidden=true\n")
notes.append(f"entri autostart lain dinonaktifkan: {os.path.basename(path)}")
except OSError:
pass
else:
if os.path.exists(AUTOSTART_FILE):
os.unlink(AUTOSTART_FILE)
notes.append("autostart tray dihapus")
for path in other_autostart_entries(appimage):
try:
lines = [l for l in open(path).read().splitlines() if l.strip() != "Hidden=true"]
with open(path, "w") as fh:
fh.write("\n".join(lines) + "\n")
notes.append(f"entri autostart lain diaktifkan kembali: {os.path.basename(path)}")
except OSError:
pass
return "; ".join(notes) or "tidak ada perubahan autostart tray"
def systemctl(*args): def systemctl(*args):
return subprocess.run(["systemctl", "--user", *args], capture_output=True, text=True) return subprocess.run(["systemctl", "--user", *args], capture_output=True, text=True)
@@ -688,8 +748,10 @@ class Window(QWidget):
systemctl("daemon-reload") systemctl("daemon-reload")
res = systemctl("enable" if checked else "disable", SERVICE) res = systemctl("enable" if checked else "disable", SERVICE)
state = service_enabled() state = service_enabled()
note = set_tray_autostart(checked, installed_appimage())
return (f"mulai otomatis saat login: {'AKTIF' if state else 'nonaktif'}" return (f"mulai otomatis saat login: {'AKTIF' if state else 'nonaktif'}"
+ ("" if res.returncode == 0 else f" ({res.stderr.strip()})")) + ("" if res.returncode == 0 else f" ({res.stderr.strip()})")
+ f" | {note}")
except Exception as exc: # noqa: BLE001 except Exception as exc: # noqa: BLE001
return f"galat autostart: {exc}" return f"galat autostart: {exc}"
+7 -3
View File
@@ -423,9 +423,10 @@ def main():
signal.signal(signal.SIGTERM, lambda *_: stop.set()) signal.signal(signal.SIGTERM, lambda *_: stop.set())
signal.signal(signal.SIGINT, lambda *_: stop.set()) signal.signal(signal.SIGINT, lambda *_: stop.set())
print(f"menunggu {VID:#06x}:{PID:#06x} (Ctrl-C atau SIGTERM untuk berhenti)", flush=True) print(f"menunggu {VID:#06x}:{PID:#06x} (Ctrl-C atau SIGTERM untuk berhenti)", flush=True)
last_message = None
while not stop.is_set(): while not stop.is_set():
if not find_device(): if not find_device():
stop.wait(2) stop.wait(1)
continue continue
try: try:
result = stream(a, overrides) result = stream(a, overrides)
@@ -433,8 +434,11 @@ def main():
sent, dt = result sent, dt = result
print(f"perangkat terlepas setelah {sent} report ({sent / dt:.1f}/detik), menunggu lagi", flush=True) print(f"perangkat terlepas setelah {sent} report ({sent / dt:.1f}/detik), menunggu lagi", flush=True)
except OSError as exc: except OSError as exc:
print(f"galat perangkat ({exc}), menunggu lagi", flush=True) message = f"galat perangkat ({exc}), menunggu lagi"
stop.wait(2) if message != last_message: # ACL udev baru aktif setelah sesi login
print(message, flush=True)
last_message = message
stop.wait(1)
print("berhenti") print("berhenti")
return 0 return 0