/* Один GUI на каталог профиля: QLockFile защищает данные, локальный канал передаёт * запрос показа владельцу. start вызывается один раз до чтения конфига/запуска ядра. * Объект живёт до уничтожения окон; все вызовы — GUI-поток. */ #pragma once #include #include #include #include #include class QWidget; /* Показать окно, сохранив maximized/fullscreen, и активировать его модальный диалог, если он есть. */ void showAndActivateWindow(QWidget* window); class SingleInstance : public QObject { public: enum Result { Error = -1, Primary, Activated }; /* profileDirectory — существующий каталог конфига; parent владеет объектом по правилам QObject. */ explicit SingleInstance(const QString& profileDirectory, QObject* parent = nullptr); ~SingleInstance() override; /* Получить блокировку и открыть канал либо дождаться ACK владельца (до 5 секунд). * Primary: продолжить запуск; Activated: завершить второй процесс с 0; Error: errorString(). */ Result start(); /* Текущее окно: мастер настройки или MainWindow. nullptr откладывает показ до следующего setWindow. */ void setWindow(QWidget* window); QString errorString() const { return m_error; } QString serverName() const { return m_name; } private: Result fail(const QString& error); Result activatePrimary(); void acceptConnections(); QLockFile m_lock; // Защита профиля на весь срок жизни первого экземпляра. QLocalServer m_server; // Только текущий пользователь; имя связано с каноническим путём профиля. QString m_name, m_error; QPointer m_window; // Заимствованное окно; обнуляется при уничтожении мастера/GUI. bool m_started = false; bool m_pending = false; // Запрос принят до создания окна; повторные запросы объединяются. };