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
This commit is contained in:
1 parent
c3d34a2788
commit
76790e0321
4 files changed
+89
-7
No files matched your search
@@ -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
@@ -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)"
|
||||||
|
|||||||
+64
-2
@@ -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.2"
|
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}"
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user