1package daemon
2
3import "github.com/bnema/vev/internal/usecase/layout"
4
5// movePaneAdmission is an immutable snapshot of the exact live objects and
6// generations a move may later commit. Resize fences are acquired only after
7// this snapshot; the locked commit revalidates every field before publication.
8type movePaneAdmission struct {
9 source *session
10 destination *session
11 sourceTab *tab
12 destinationTab *tab
13 movedPane *pane
14 sourceAttachments []*attachedClient
15 sourceTransports map[*attachedClient]transportSnapshot
16 sourceGeneration uint64
17 destinationGeneration uint64
18 finalSourceTab bool
19}
20
21func (d *Daemon) snapshotMovePaneAdmission(req movePaneRequest, source, destination *session) (*movePaneAdmission, error) {
22 d.mu.Lock()
23 defer d.mu.Unlock()
24 if d.closing || d.sessions[source.id] != source || d.sessions[destination.id] != destination {
25 return nil, errMoveStaleTarget
26 }
27 unlockSessions := lockAttachmentSessions(source, destination)
28 defer unlockSessions()
29 if !moveSessionLocatorCurrentLocked(source, req.Source) || !moveSessionLocatorCurrentLocked(destination, req.Destination) {
30 return nil, errMoveStaleTarget
31 }
32 if req.Attachment != nil && !attachmentRegisteredLocked(source, req.Attachment) {
33 return nil, errMoveStaleTarget
34 }
35 if req.AttachmentToken.ac != nil && (req.AttachmentToken.ac != req.Attachment || !moveAttachmentTokenCurrentLocked(req.AttachmentToken, source)) {
36 return nil, errMoveStaleTarget
37 }
38
39 sourceTab := findMoveTabLocked(source, req.SourceTabID)
40 destinationTab := findMoveTabLocked(destination, req.DestinationTabID)
41 if sourceTab == nil || destinationTab == nil {
42 return nil, errMoveStaleTarget
43 }
44 if sourceTab == destinationTab {
45 return nil, errMovePaneInvalid
46 }
47 unlockTabs := lockMoveTabs(sourceTab, destinationTab)
48 defer unlockTabs()
49
50 var movedPane *pane
51 for _, candidate := range sourceTab.panes {
52 if candidate != nil && candidate.stableID == string(req.SourcePaneID) {
53 movedPane = candidate
54 break
55 }
56 }
57 if movedPane == nil {
58 return nil, errMoveStaleTarget
59 }
60
61 attachments := source.snapshotAttachmentsLocked()
62 transports := make(map[*attachedClient]transportSnapshot, len(attachments))
63 for _, ac := range attachments {
64 transports[ac] = ac.transportSnapshot()
65 }
66 return &movePaneAdmission{
67 source: source, destination: destination, sourceTab: sourceTab, destinationTab: destinationTab,
68 movedPane: movedPane, sourceAttachments: attachments, sourceTransports: transports,
69 sourceGeneration: sourceTab.layoutGeneration, destinationGeneration: destinationTab.layoutGeneration,
70 finalSourceTab: len(source.tabs) == 1 && source.tabs[0] == sourceTab &&
71 sourceTab.tree != nil && sourceTab.tree.Root != nil && len(layout.LeafIDs(sourceTab.tree.Root)) == 1,
72 }, nil
73}