github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/darwin/single_instance.go (about) 1 //go:build darwin 2 // +build darwin 3 4 package darwin 5 6 /* 7 #cgo CFLAGS: -x objective-c 8 #cgo LDFLAGS: -framework Foundation -framework Cocoa 9 #import "ApDelegate.h" 10 11 */ 12 import "C" 13 14 import ( 15 "encoding/json" 16 "fmt" 17 "os" 18 "strings" 19 "syscall" 20 "unsafe" 21 22 "github.com/secoba/wails/v2/pkg/options" 23 ) 24 25 func SetupSingleInstance(uniqueID string) { 26 lockFilePath := getTempDir() 27 lockFileName := uniqueID + ".lock" 28 _, err := createLockFile(lockFilePath + "/" + lockFileName) 29 // if lockFile exist – send notification to second instance 30 if err != nil { 31 c := NewCalloc() 32 defer c.Free() 33 singleInstanceUniqueId := c.String(uniqueID) 34 35 data, err := options.NewSecondInstanceData() 36 if err != nil { 37 return 38 } 39 40 serialized, err := json.Marshal(data) 41 if err != nil { 42 return 43 } 44 45 C.SendDataToFirstInstance(singleInstanceUniqueId, c.String(string(serialized))) 46 47 os.Exit(0) 48 } 49 } 50 51 //export HandleSecondInstanceData 52 func HandleSecondInstanceData(secondInstanceMessage *C.char) { 53 message := C.GoString(secondInstanceMessage) 54 55 var secondInstanceData options.SecondInstanceData 56 57 err := json.Unmarshal([]byte(message), &secondInstanceData) 58 if err == nil { 59 secondInstanceBuffer <- secondInstanceData 60 } 61 } 62 63 // CreateLockFile tries to create a file with given name and acquire an 64 // exclusive lock on it. If the file already exists AND is still locked, it will 65 // fail. 66 func createLockFile(filename string) (*os.File, error) { 67 file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0o600) 68 if err != nil { 69 fmt.Printf("Failed to open lockfile %s: %s", filename, err) 70 return nil, err 71 } 72 73 err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) 74 if err != nil { 75 // Flock failed for some other reason than other instance already lock it. Print it in logs for possible debugging. 76 if !strings.Contains(err.Error(), "resource temporarily unavailable") { 77 fmt.Printf("Failed to lock lockfile %s: %s", filename, err) 78 } 79 file.Close() 80 return nil, err 81 } 82 83 return file, nil 84 } 85 86 // If app is sandboxed, golang os.TempDir() will return path that will not be accessible. So use native macOS temp dir function. 87 func getTempDir() string { 88 cstring := C.GetMacOsNativeTempDir() 89 path := C.GoString(cstring) 90 C.free(unsafe.Pointer(cstring)) 91 92 return path 93 }