diff --git a/sources/instancemanager.cpp b/sources/instancemanager.cpp index 52f28c3..f9dc5e2 100644 --- a/sources/instancemanager.cpp +++ b/sources/instancemanager.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include @@ -366,6 +368,36 @@ void InstanceManager::notifyPathChanged(const QStringList& paths) { } } +bool InstanceManager::eventFilter(QObject* watched, QEvent* event) { + if (event->type() == QEvent::FileOpen) { + QString path = static_cast(event)->file(); + if (!path.isEmpty()) { + if (m_role == Role::Mothership) { + handleOpenRequest(path); + } else if (m_role == Role::Worker) { + // Reuse the persistent mothership connection: from the mothership's + // side this is indistinguishable from a Windows forwarder's + // Command::Open, so it goes through the exact same + // already-open-elsewhere-vs-spawn-a-new-worker logic. + // 既存の母艦への持続的な接続を再利用する: 母艦側から見れば + // これはWindowsの転送プロセスによるCommand::Openと区別が + // つかないため、既存の「他で開いているか・新規ワーカーを + // 起動するか」の判定ロジックがそのまま適用される。 + sendFrame(m_workerSocket, Command::Open, path); + } + // Role::Standalone: no mothership exists to route this to (IPC setup + // failed entirely); there is no reasonable fallback, so the request + // is silently dropped, same as it would have been before this filter + // existed. + // Role::Standalone: 転送先の母艦が存在しない(IPCの初期化自体が + // 失敗している)ため、妥当な代替手段がなく、このフィルタ導入前と + // 同様に要求を静かに無視する。 + } + return true; + } + return QObject::eventFilter(watched, event); +} + void InstanceManager::requestUnlink() { if (m_role != Role::Worker || !m_workerSocket) return; sendFrame(m_workerSocket, Command::Unlink, QString()); diff --git a/sources/instancemanager.h b/sources/instancemanager.h index ed0e781..7d002f0 100644 --- a/sources/instancemanager.h +++ b/sources/instancemanager.h @@ -12,6 +12,7 @@ class QLocalServer; class QLocalSocket; class QSharedMemory; +class QEvent; // Coordinates single-instance behavior for the app: a single hidden // "mothership" process arbitrates which XDTS file is open in which @@ -65,6 +66,22 @@ class InstanceManager : public QObject { // (以後のCSP同期にのみ影響)なので、呼び出し前の確認は不要。 void requestUnlink(); + // macOS only: Finder/`open` file-open requests for a file other than the + // one this process was launched with arrive as a QFileOpenEvent on the + // running QApplication instead of a fresh process with a new argv (macOS's + // Launch Services treats the bundle as already running and does not start + // a second process). Installed as an application-wide event filter by + // main.cpp so it sees these regardless of which existing process (the + // mothership or a worker) macOS happens to deliver them to. + // macOS専用: 起動時に渡されたのとは別のファイルに対するFinder/`open`の + // オープン要求は、新しいargvを持つ新規プロセスとしてではなく、既存の + // QApplicationインスタンスへのQFileOpenEventとして届く(macOSの + // Launch Servicesはバンドルを既に実行中とみなし、2つ目のプロセスを + // 起動しないため)。macOSがどのプロセス(母艦かワーカーか)にこれを + // 配送するかに関わらず受け取れるよう、main.cppからアプリケーション + // 全体のイベントフィルタとしてインストールされる。 + bool eventFilter(QObject* watched, QEvent* event) override; + signals: // Worker role only: emitted when the mothership asks this window to // reload its XDTS data and come to the foreground, because another diff --git a/sources/main.cpp b/sources/main.cpp index 59d1a71..f1cf981 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -54,6 +54,22 @@ int main(int argc, char* argv[]) { InstanceManager::Role role = InstanceManager::instance()->bootstrap(a.arguments(), initialPath); if (role == InstanceManager::Role::Forwarded) return 0; + +#ifdef __MACOS__ + // On macOS, Finder/`open` requests for a file other than the one this + // process was launched with don't start a new process (Launch Services + // treats the bundle as already running); they arrive as a + // QFileOpenEvent on whichever process is already running instead. This + // has no effect on Windows, where every launch is a fresh process with + // its own argv. + // macOSでは、起動時に渡されたのとは別のファイルに対するFinder/`open`の + // 要求は新規プロセスを起動しない(Launch Servicesがバンドルを既に + // 実行中とみなすため)。代わりに、既に実行中のいずれかのプロセスへ + // QFileOpenEventとして届く。各起動が固有のargvを持つ新規プロセスと + // なるWindowsでは影響しない。 + qApp->installEventFilter(InstanceManager::instance()); +#endif + if (role == InstanceManager::Role::Mothership) return a.exec(); MyParams::instance()->initStamps();