|
133 | 133 | MAIN_DIRECTORY = str(Path(__file__).parent.resolve()) + "/" |
134 | 134 | THEMES_DIR = MAIN_DIRECTORY + 'res/themes' |
135 | 135 | SMARTMONITOR_THEMES_DIR = MAIN_DIRECTORY + 'res/smartmonitor/themes' |
| 136 | +AUTOSTART_SERVICE_NAME = "turing-smart-screen-python.service" |
136 | 137 | DEFAULT_SMARTMONITOR_VENDOR_THEME_ROOT = ( |
137 | 138 | os.path.join(MAIN_DIRECTORY, "vendor", "themefor3.5") |
138 | 139 | if os.path.isdir(os.path.join(MAIN_DIRECTORY, "vendor", "themefor3.5")) |
@@ -337,6 +338,81 @@ def sanitize_smartmonitor_theme_name(name: str) -> str: |
337 | 338 | return "".join(safe).strip() |
338 | 339 |
|
339 | 340 |
|
| 341 | +def autostart_supported() -> bool: |
| 342 | + return platform.system() == "Linux" |
| 343 | + |
| 344 | + |
| 345 | +def autostart_service_dir() -> Path: |
| 346 | + return Path.home() / ".config" / "systemd" / "user" |
| 347 | + |
| 348 | + |
| 349 | +def autostart_service_path() -> Path: |
| 350 | + return autostart_service_dir() / AUTOSTART_SERVICE_NAME |
| 351 | + |
| 352 | + |
| 353 | +def render_autostart_service() -> str: |
| 354 | + python_exec = Path(sys.executable).resolve() |
| 355 | + main_script = Path(MAIN_DIRECTORY) / "main.py" |
| 356 | + working_dir = Path(MAIN_DIRECTORY).resolve() |
| 357 | + return "\n".join([ |
| 358 | + "[Unit]", |
| 359 | + "Description=Turing Smart Screen Python HIDdev", |
| 360 | + "After=graphical-session.target network-online.target", |
| 361 | + "Wants=graphical-session.target network-online.target", |
| 362 | + "", |
| 363 | + "[Service]", |
| 364 | + "Type=simple", |
| 365 | + f"WorkingDirectory={working_dir}", |
| 366 | + f"ExecStart={python_exec} {main_script}", |
| 367 | + "Restart=on-failure", |
| 368 | + "RestartSec=3", |
| 369 | + "Environment=PYTHONUNBUFFERED=1", |
| 370 | + "", |
| 371 | + "[Install]", |
| 372 | + "WantedBy=default.target", |
| 373 | + "", |
| 374 | + ]) |
| 375 | + |
| 376 | + |
| 377 | +def write_autostart_service_file() -> Path: |
| 378 | + service_dir = autostart_service_dir() |
| 379 | + service_dir.mkdir(parents=True, exist_ok=True) |
| 380 | + service_path = autostart_service_path() |
| 381 | + service_path.write_text(render_autostart_service(), encoding="utf-8") |
| 382 | + return service_path |
| 383 | + |
| 384 | + |
| 385 | +def is_autostart_enabled() -> bool: |
| 386 | + if not autostart_supported(): |
| 387 | + return False |
| 388 | + try: |
| 389 | + result = subprocess.run( |
| 390 | + ["systemctl", "--user", "is-enabled", AUTOSTART_SERVICE_NAME], |
| 391 | + stdout=subprocess.PIPE, |
| 392 | + stderr=subprocess.PIPE, |
| 393 | + text=True, |
| 394 | + check=False, |
| 395 | + ) |
| 396 | + return result.returncode == 0 and result.stdout.strip() == "enabled" |
| 397 | + except Exception: |
| 398 | + return autostart_service_path().is_file() |
| 399 | + |
| 400 | + |
| 401 | +def enable_autostart_service(): |
| 402 | + service_path = write_autostart_service_file() |
| 403 | + subprocess.run(["systemctl", "--user", "daemon-reload"], check=True) |
| 404 | + subprocess.run(["systemctl", "--user", "enable", "--now", AUTOSTART_SERVICE_NAME], check=True) |
| 405 | + return service_path |
| 406 | + |
| 407 | + |
| 408 | +def disable_autostart_service(): |
| 409 | + subprocess.run(["systemctl", "--user", "disable", "--now", AUTOSTART_SERVICE_NAME], check=False) |
| 410 | + service_path = autostart_service_path() |
| 411 | + if service_path.exists(): |
| 412 | + service_path.unlink() |
| 413 | + subprocess.run(["systemctl", "--user", "daemon-reload"], check=False) |
| 414 | + |
| 415 | + |
340 | 416 | def smartmonitor_theme_dir(name: str) -> str: |
341 | 417 | return os.path.join(SMARTMONITOR_THEMES_DIR, name) |
342 | 418 |
|
@@ -751,11 +827,25 @@ def __init__(self): |
751 | 827 | ) |
752 | 828 | self.new_ui_btn.place(x=570, y=520, height=50, width=100) |
753 | 829 |
|
| 830 | + self.autostart_enable_btn = ttk.Button( |
| 831 | + self.window, |
| 832 | + text="Enable\nautostart", |
| 833 | + command=lambda: self.on_enable_autostart_click(), |
| 834 | + ) |
| 835 | + self.autostart_enable_btn.place(x=680, y=520, height=50, width=100) |
| 836 | + |
| 837 | + self.autostart_disable_btn = ttk.Button( |
| 838 | + self.window, |
| 839 | + text="Disable\nautostart", |
| 840 | + command=lambda: self.on_disable_autostart_click(), |
| 841 | + ) |
| 842 | + self.autostart_disable_btn.place(x=790, y=520, height=50, width=100) |
| 843 | + |
754 | 844 | self.save_btn = ttk.Button(self.window, text="Save settings", command=lambda: self.on_save_click()) |
755 | | - self.save_btn.place(x=680, y=520, height=50, width=110) |
| 845 | + self.save_btn.place(x=900, y=520, height=50, width=110) |
756 | 846 |
|
757 | 847 | self.save_run_btn = ttk.Button(self.window, text="Save and run", command=lambda: self.on_saverun_click()) |
758 | | - self.save_run_btn.place(x=800, y=520, height=50, width=110) |
| 848 | + self.save_run_btn.place(x=1020, y=520, height=50, width=110) |
759 | 849 |
|
760 | 850 | self.config = None |
761 | 851 | self.load_config_values() |
@@ -790,6 +880,7 @@ def refresh_theme_selector(self): |
790 | 880 | self.theme_cb.set("") |
791 | 881 | elif self.theme_cb.get() not in themes: |
792 | 882 | self.theme_cb.set(themes[0]) |
| 883 | + self.refresh_autostart_buttons() |
793 | 884 |
|
794 | 885 | def load_theme_preview(self): |
795 | 886 | if self.is_smartmonitor_model(): |
@@ -1039,6 +1130,66 @@ def on_edit_ui_click(self): |
1039 | 1130 | def on_save_click(self): |
1040 | 1131 | self.save_config_values() |
1041 | 1132 |
|
| 1133 | + def refresh_autostart_buttons(self): |
| 1134 | + if not autostart_supported(): |
| 1135 | + self.autostart_enable_btn.state(["disabled"]) |
| 1136 | + self.autostart_disable_btn.state(["disabled"]) |
| 1137 | + return |
| 1138 | + |
| 1139 | + enabled = is_autostart_enabled() |
| 1140 | + if enabled: |
| 1141 | + self.autostart_enable_btn.state(["disabled"]) |
| 1142 | + self.autostart_disable_btn.state(["!disabled"]) |
| 1143 | + else: |
| 1144 | + self.autostart_enable_btn.state(["!disabled"]) |
| 1145 | + self.autostart_disable_btn.state(["disabled"]) |
| 1146 | + |
| 1147 | + def on_enable_autostart_click(self): |
| 1148 | + if not autostart_supported(): |
| 1149 | + messagebox.showinfo("Autostart", "Autostart via systemd --user is available only on Linux.", parent=self.window) |
| 1150 | + return |
| 1151 | + |
| 1152 | + try: |
| 1153 | + service_path = enable_autostart_service() |
| 1154 | + except subprocess.CalledProcessError as exc: |
| 1155 | + messagebox.showerror( |
| 1156 | + "Autostart failed", |
| 1157 | + "Could not enable autostart via systemd --user.\n\n" |
| 1158 | + f"Service file:\n{autostart_service_path()}\n\n" |
| 1159 | + f"Error: {exc}", |
| 1160 | + parent=self.window, |
| 1161 | + ) |
| 1162 | + return |
| 1163 | + except Exception as exc: |
| 1164 | + messagebox.showerror("Autostart failed", str(exc), parent=self.window) |
| 1165 | + return |
| 1166 | + |
| 1167 | + self.refresh_autostart_buttons() |
| 1168 | + messagebox.showinfo( |
| 1169 | + "Autostart enabled", |
| 1170 | + "Autostart has been enabled for the current user.\n\n" |
| 1171 | + f"Service file:\n{service_path}", |
| 1172 | + parent=self.window, |
| 1173 | + ) |
| 1174 | + |
| 1175 | + def on_disable_autostart_click(self): |
| 1176 | + if not autostart_supported(): |
| 1177 | + messagebox.showinfo("Autostart", "Autostart via systemd --user is available only on Linux.", parent=self.window) |
| 1178 | + return |
| 1179 | + |
| 1180 | + try: |
| 1181 | + disable_autostart_service() |
| 1182 | + except Exception as exc: |
| 1183 | + messagebox.showerror("Autostart disable failed", str(exc), parent=self.window) |
| 1184 | + return |
| 1185 | + |
| 1186 | + self.refresh_autostart_buttons() |
| 1187 | + messagebox.showinfo( |
| 1188 | + "Autostart disabled", |
| 1189 | + "Autostart has been disabled for the current user.", |
| 1190 | + parent=self.window, |
| 1191 | + ) |
| 1192 | + |
1042 | 1193 | def stop_running_main_instances(self): |
1043 | 1194 | main_scripts = { |
1044 | 1195 | os.path.abspath(os.path.join(MAIN_DIRECTORY, script_name)) |
|
0 commit comments