1package vt
2
3func (s *Screen) Resize(width, height int) {
4 if width == s.frame.Width && height == s.frame.Height {
5 s.geometry.Cols, s.geometry.Rows = width, height
6 return
7 }
8 if width <= 0 || height <= 0 {
9 // renderer.Frame permits a collapsed geometry. There is no layout to
10 // preserve until a usable viewport is supplied again.
11 width, height = max(width, 0), max(height, 0)
12 s.buffer = s.newBuffer(width, height)
13 s.Row, s.Col = 0, 0
14 if state := s.alternate; state != nil {
15 state.buffer = s.newBuffer(width, height)
16 state.row, state.col = 0, 0
17 state.scrollTop, state.scrollBottom = 0, height-1
18 }
19 } else {
20 resize := func(b *buffer, row, col int, saved *cursorState, evict bool) (int, int) {
21 active := &bufferCursor{row: row, col: col}
22 var savedPoint *bufferCursor
23 if saved != nil && saved.saved {
24 savedPoint = &bufferCursor{row: saved.row, col: saved.col}
25 }
26 evicted, evictedBounds, evictedIDs := b.resize(width, height, active, savedPoint)
27 for i := range evictedIDs {
28 if evictedIDs[i] == 0 {
29 evictedIDs[i] = s.nextRowIDValue()
30 }
31 }
32 s.fillMissingRowIDs(b)
33 if evict {
34 for i, line := range evicted {
35 s.recordEvicted(line, evictedBounds[i], evictedIDs[i])
36 }
37 }
38 if savedPoint != nil {
39 saved.row, saved.col = savedPoint.row, clamp(savedPoint.col, 0, width-1)
40 }
41 return active.row, clamp(active.col, 0, width-1)
42 }
43
44 if s.alternate != nil {
45 // Both screen states are reflowed independently, but active and saved
46 // cursors for each state are mapped by their single buffer layout pass.
47 s.Row, s.Col = resize(s.buffer, s.Row, s.Col, &s.savedCursor, false)
48 state := s.alternate
49 state.row, state.col = resize(state.buffer, state.row, state.col, &state.savedCursor, true)
50 state.scrollTop, state.scrollBottom = 0, height-1
51 } else {
52 s.Row, s.Col = resize(s.buffer, s.Row, s.Col, &s.savedCursor, true)
53 }
54 }
55 s.frame = s.buffer.frame
56 s.geometry.Cols, s.geometry.Rows = width, height
57 // A resize can split an in-flight escape sequence from the terminal state it
58 // was meant to mutate; keep the durable child state but discard partial bytes.
59 s.escapeBuf = s.escapeBuf[:0]
60 s.kittyDiscard = false
61 s.kittyDiscardEscaped = false
62 s.abortAllKittyPending()
63 s.resetScrollRegion()
64 s.fullRedraw()
65}
66
67func clamp(v, lo, hi int) int {
68 if hi < lo {
69 return lo
70 }
71 if v < lo {
72 return lo
73 }
74 if v > hi {
75 return hi
76 }
77 return v
78}