Started without a command, keeporg now shows a padlock in the system tray with a menu like midi-webhook's: a status line, the mirror folder, each database with its state, Sync Now, browser sign-in and sign-out, the web interface, the configuration, the log, Start at Login, About and Quit. The commands are unchanged. config.json now carries every client setting (device_label, request_timeout_seconds, ca_file, log_file, notifications), is written in full on first start and is reloaded by the tray when saved. A lock in the mirror directory admits one agent at a time, since the tray, `keeporg run` and a service unit could otherwise race each other. The tray uses fyne.io/systray: pure Go on Linux, BSD and Windows, cgo on macOS only. The server keeps no third-party dependencies. Desktop versions go to 0.5.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
31 lines
741 B
Go
31 lines
741 B
Go
//go:build windows
|
|
|
|
package client
|
|
|
|
import (
|
|
"errors"
|
|
"syscall"
|
|
)
|
|
|
|
// lockPath opens the file with no sharing at all, which Windows enforces
|
|
// until the handle closes.
|
|
func lockPath(path string) (func(), error) {
|
|
p, err := syscall.UTF16PtrFromString(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h, err := syscall.CreateFile(p, syscall.GENERIC_READ|syscall.GENERIC_WRITE,
|
|
0, nil, syscall.OPEN_ALWAYS, syscall.FILE_ATTRIBUTE_HIDDEN, 0)
|
|
if err != nil {
|
|
if errors.Is(err, errSharingViolation) {
|
|
return nil, ErrMirrorBusy
|
|
}
|
|
return nil, err
|
|
}
|
|
return func() { syscall.CloseHandle(h) }, nil
|
|
}
|
|
|
|
// errSharingViolation is ERROR_SHARING_VIOLATION, which package syscall
|
|
// does not name.
|
|
const errSharingViolation = syscall.Errno(32)
|