diff --git a/lib/main.dart b/lib/main.dart index 2ce831bd..915b363b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,70 @@ +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:umbrix/bootstrap.dart'; import 'package:umbrix/core/model/environment.dart'; +final lockFile = File('/tmp/umbrix.lock'); + +Future _activateExistingWindow() async { + // Try to activate existing window via wmctrl + try { + final result = await Process.run('wmctrl', ['-a', 'Umbrix']); + if (result.exitCode == 0) return; + } catch (e) { + // wmctrl not available, try xdotool + try { + await Process.run('xdotool', ['search', '--name', 'Umbrix', 'windowactivate']); + } catch (e) { + // No window activation possible + } + } +} + +void _cleanupLockFile() { + try { + if (lockFile.existsSync()) { + lockFile.deleteSync(); + } + } catch (e) { + // Ignore cleanup errors + } +} + void main() async { + // Single instance check - BEFORE Flutter initialization + if (Platform.isLinux || Platform.isWindows) { + if (await lockFile.exists()) { + // Check if process is still alive + try { + final pidString = await lockFile.readAsString(); + final pid = int.tryParse(pidString.trim()); + if (pid != null) { + final result = await Process.run('ps', ['-p', pid.toString()]); + if (result.exitCode != 0) { + // Process dead, remove stale lock + await lockFile.delete(); + } else { + // Process alive - activate window and exit + await _activateExistingWindow(); + exit(0); + } + } + } catch (e) { + // Error reading lock, remove it + try { await lockFile.delete(); } catch (e) {} + } + } + + // Create lock file + try { + await lockFile.create(); + await lockFile.writeAsString(pid.toString()); + } catch (e) { + // If can't create, continue anyway + } + } + final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);