From 31ace555d059e5cd5a9d51a34bd190ed8a13dd6b Mon Sep 17 00:00:00 2001 From: Boris Pozdena Date: Mon, 24 Nov 2025 12:59:50 +0100 Subject: [PATCH] Autostart OneDriveGUI after login --- src/gui_settings_window.py | 66 +++++++++++++ src/settings/gui_settings.py | 1 + src/ui/gui_settings_window.ui | 7 ++ src/ui/ui_gui_settings_window.py | 12 ++- src/utils/autostart.py | 163 +++++++++++++++++++++++++++++++ 5 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 src/utils/autostart.py diff --git a/src/gui_settings_window.py b/src/gui_settings_window.py index 47b2020..01ae3c8 100644 --- a/src/gui_settings_window.py +++ b/src/gui_settings_window.py @@ -28,6 +28,9 @@ class GuiSettingsWindow(QWidget, Ui_gui_settings_window): self.checkBox_start_minimized.setChecked(self.get_check_box_state("start_minimized")) self.checkBox_start_minimized.stateChanged.connect(self.set_check_box_state) + self.checkBox_autostart_enabled.setChecked(self.get_check_box_state("autostart_enabled")) + self.checkBox_autostart_enabled.stateChanged.connect(self.handle_autostart_change) + self.lineEdit_client_bin_path.setText(gui_settings.get("client_bin_path")) self.lineEdit_client_bin_path.textChanged.connect(self.set_client_bin_path) self.pushButton_client_bin_path.clicked.connect(self.get_bin_path) @@ -63,6 +66,9 @@ class GuiSettingsWindow(QWidget, Ui_gui_settings_window): self.pushButton_save.clicked.connect(self.save_settings) + # Sync autostart file with saved setting on startup + self.sync_autostart_on_startup() + def get_bin_path(self): self.file_dialog = QFileDialog() self.file_dialog.setFileMode(QFileDialog.ExistingFile) @@ -111,8 +117,68 @@ class GuiSettingsWindow(QWidget, Ui_gui_settings_window): logging.info(f"[GUI][SETTINGS] {property} is unchecked") gui_settings.set(property, "False") + def sync_autostart_on_startup(self): + """Sync autostart file with saved setting on app startup.""" + from utils.autostart import enable_autostart, disable_autostart, is_autostart_enabled + + # Get the saved setting + autostart_should_be_enabled = self.get_check_box_state("autostart_enabled") + autostart_is_enabled = is_autostart_enabled() + + # If they don't match, sync them + if autostart_should_be_enabled and not autostart_is_enabled: + logging.info("[GUI][SETTINGS] Autostart is enabled in settings but desktop file is missing - creating it") + success = enable_autostart() + if not success: + logging.error("[GUI][SETTINGS] Failed to create autostart file on startup") + elif not autostart_should_be_enabled and autostart_is_enabled: + logging.info("[GUI][SETTINGS] Autostart is disabled in settings but desktop file exists - removing it") + success = disable_autostart() + if not success: + logging.error("[GUI][SETTINGS] Failed to remove autostart file on startup") + else: + logging.info(f"[GUI][SETTINGS] Autostart file is in sync with settings (enabled: {autostart_should_be_enabled})") + + def handle_autostart_change(self): + """Handle autostart checkbox state change - only updates settings, doesn't create/delete files.""" + if self.sender().isChecked(): + logging.info("[GUI][SETTINGS] Autostart checkbox checked") + gui_settings.set("autostart_enabled", "True") + else: + logging.info("[GUI][SETTINGS] Autostart checkbox unchecked") + gui_settings.set("autostart_enabled", "False") + def save_settings(self): + """Save all settings and sync autostart file with checkbox state.""" + from utils.autostart import enable_autostart, disable_autostart + from PySide6.QtWidgets import QMessageBox + + # Save all settings first gui_settings.save() + # Now sync the autostart file with the saved setting + autostart_should_be_enabled = self.checkBox_autostart_enabled.isChecked() + + if autostart_should_be_enabled: + logging.info("[GUI][SETTINGS] Creating autostart file") + success = enable_autostart() + if not success: + logging.error("[GUI][SETTINGS] Failed to create autostart file") + QMessageBox.warning( + self, + "Autostart Error", + "Settings saved, but failed to create autostart file. Please check the logs for details.", + ) + else: + logging.info("[GUI][SETTINGS] Removing autostart file") + success = disable_autostart() + if not success: + logging.error("[GUI][SETTINGS] Failed to remove autostart file") + QMessageBox.warning( + self, + "Autostart Error", + "Settings saved, but failed to remove autostart file. Please check the logs for details.", + ) + gui_settings_window = GuiSettingsWindow() diff --git a/src/settings/gui_settings.py b/src/settings/gui_settings.py index 4540dbb..56f26b0 100644 --- a/src/settings/gui_settings.py +++ b/src/settings/gui_settings.py @@ -17,6 +17,7 @@ class GuiSettings: "debug_level": "DEBUG", "client_bin_path": "onedrive", "QWebEngine_login": "False", + "autostart_enabled": "False", } } diff --git a/src/ui/gui_settings_window.ui b/src/ui/gui_settings_window.ui index b853609..d33b182 100644 --- a/src/ui/gui_settings_window.ui +++ b/src/ui/gui_settings_window.ui @@ -44,6 +44,13 @@ + + + + Start OneDriveGUI automatically on login + + + diff --git a/src/ui/ui_gui_settings_window.py b/src/ui/ui_gui_settings_window.py index 09c4e21..3b80462 100644 --- a/src/ui/ui_gui_settings_window.py +++ b/src/ui/ui_gui_settings_window.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- ################################################################################ -## Form generated from reading UI file 'gui_settings_windowfuZzTA.ui' +## Form generated from reading UI file 'gui_settings_window.ui' ## -## Created by: Qt User Interface Compiler version 6.7.2 +## Created by: Qt User Interface Compiler version 6.10.0 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -44,7 +44,12 @@ class Ui_gui_settings_window(object): self.checkBox_start_minimized = QCheckBox(self.groupBox) self.checkBox_start_minimized.setObjectName(u"checkBox_start_minimized") - self.formLayout.setWidget(0, QFormLayout.LabelRole, self.checkBox_start_minimized) + self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.checkBox_start_minimized) + + self.checkBox_autostart_enabled = QCheckBox(self.groupBox) + self.checkBox_autostart_enabled.setObjectName(u"checkBox_autostart_enabled") + + self.formLayout.setWidget(1, QFormLayout.ItemRole.LabelRole, self.checkBox_autostart_enabled) self.verticalLayout_3.addLayout(self.formLayout) @@ -228,6 +233,7 @@ class Ui_gui_settings_window(object): gui_settings_window.setWindowTitle(QCoreApplication.translate("gui_settings_window", u"OneDriveGUI - Settings", None)) self.groupBox.setTitle(QCoreApplication.translate("gui_settings_window", u"OneDriveGUI behaviour", None)) self.checkBox_start_minimized.setText(QCoreApplication.translate("gui_settings_window", u"Start OneDriveGUI minimized", None)) + self.checkBox_autostart_enabled.setText(QCoreApplication.translate("gui_settings_window", u"Start OneDriveGUI automatically on login", None)) self.groupBox_4.setTitle(QCoreApplication.translate("gui_settings_window", u"OneDrive client options", None)) self.label_client_bin_path.setText(QCoreApplication.translate("gui_settings_window", u"Path to OneDrive client binary", None)) self.pushButton_client_bin_path.setText(QCoreApplication.translate("gui_settings_window", u"Browse", None)) diff --git a/src/utils/autostart.py b/src/utils/autostart.py new file mode 100644 index 0000000..bfb628e --- /dev/null +++ b/src/utils/autostart.py @@ -0,0 +1,163 @@ +""" +Autostart management for OneDriveGUI. + +Handles creation and removal of XDG autostart desktop entries. +Supports both AppImage and source installations. +""" + +import os +import logging +from pathlib import Path + + +def is_running_in_appimage() -> bool: + """ + Detect if running inside an AppImage. + + Returns: + bool: True if running in AppImage, False otherwise + """ + return os.getenv("APPIMAGE") is not None + + +def get_executable_path() -> str: + """ + Get the absolute path to the executable. + + For AppImage installations, returns the path to the AppImage file. + For source/system installations, returns 'OneDriveGUI' (assumes it's in PATH). + + Returns: + str: Executable path or command + """ + appimage_path = os.getenv("APPIMAGE") + if appimage_path: + # Running in AppImage - use the AppImage path + # $APPIMAGE already resolves symlinks + return appimage_path + else: + # Running from source or system installation + # Use command name from PATH (works for AUR and other package managers) + return "OneDriveGUI" + + +def get_autostart_file_path() -> Path: + """ + Get the path to the autostart desktop file. + + Returns: + Path: Path to ~/.config/autostart/OneDriveGUI.desktop + """ + config_dir = Path.home() / ".config" / "autostart" + return config_dir / "OneDriveGUI.desktop" + + +def is_autostart_enabled() -> bool: + """ + Check if autostart is currently enabled. + + Returns: + bool: True if autostart desktop file exists and is not hidden + """ + autostart_file = get_autostart_file_path() + if not autostart_file.exists(): + return False + + # Check if the desktop file has Hidden=true + try: + with open(autostart_file, "r") as f: + content = f.read() + # If Hidden=true is present, autostart is disabled + if "Hidden=true" in content: + return False + return True + except Exception as e: + logging.error(f"Error checking autostart status: {e}") + return False + + +def enable_autostart() -> bool: + """ + Enable autostart by creating the desktop file. + + Creates ~/.config/autostart/OneDriveGUI.desktop with the appropriate + executable path for either AppImage or source installation. + + Returns: + bool: True if successful, False otherwise + """ + try: + autostart_file = get_autostart_file_path() + autostart_dir = autostart_file.parent + + # Create autostart directory if it doesn't exist + autostart_dir.mkdir(parents=True, exist_ok=True) + + # Get the correct executable path + exec_path = get_executable_path() + + # Create desktop file content + desktop_content = f"""[Desktop Entry] +Type=Application +Version=1.0 +Name=OneDriveGUI +Comment=A simple GUI for OneDrive Linux client +Exec={exec_path} +Icon=OneDriveGUI +Terminal=false +Categories=Network;Office; +StartupNotify=false +Hidden=false +""" + + # Write the desktop file + with open(autostart_file, "w") as f: + f.write(desktop_content) + + logging.info(f"Autostart enabled: {autostart_file}") + logging.info(f"Using executable: {exec_path}") + return True + + except Exception as e: + logging.error(f"Failed to enable autostart: {e}") + return False + + +def disable_autostart() -> bool: + """ + Disable autostart by removing the desktop file. + + Removes ~/.config/autostart/OneDriveGUI.desktop if it exists. + + Returns: + bool: True if successful, False otherwise + """ + try: + autostart_file = get_autostart_file_path() + + if autostart_file.exists(): + autostart_file.unlink() + logging.info(f"Autostart disabled: {autostart_file}") + else: + logging.info("Autostart file does not exist, nothing to disable") + + return True + + except Exception as e: + logging.error(f"Failed to disable autostart: {e}") + return False + + +def get_autostart_info() -> dict: + """ + Get information about the current autostart configuration. + + Returns: + dict: Information about autostart status and paths + """ + return { + "enabled": is_autostart_enabled(), + "desktop_file": str(get_autostart_file_path()), + "executable": get_executable_path(), + "is_appimage": is_running_in_appimage(), + }