1package uv
2
3import (
4 "context"
5
6 "github.com/charmbracelet/x/term"
7 "golang.org/x/sync/errgroup"
8)
9
10// Size represents the size of the terminal window.
11type Size struct {
12 Width int
13 Height int
14}
15
16// Bounds returns the bounds corresponding to the size.
17func (s Size) Bounds() Rectangle {
18 return Rect(0, 0, s.Width, s.Height)
19}
20
21// InputReceiver is an interface for receiving input events from an input source.
22type InputReceiver interface {
23 // ReceiveEvents read input events and channel them to the given event
24 // channel. The listener stops when either the context is done or an error
25 // occurs. Caller is responsible for closing the channels.
26 ReceiveEvents(ctx context.Context, events chan<- Event) error
27}
28
29// InputManager manages input events from multiple input sources. It listens
30// for input events from the registered input sources and combines them into a
31// single event channel. It also handles errors from the input sources and
32// sends them to the error channel.
33type InputManager struct {
34 receivers []InputReceiver
35}
36
37// NewInputManager creates a new InputManager with the input receivers.
38func NewInputManager(receivers ...InputReceiver) *InputManager {
39 im := &InputManager{
40 receivers: receivers,
41 }
42 return im
43}
44
45// RegisterReceiver registers a new input receiver with the input manager.
46func (im *InputManager) RegisterReceiver(r InputReceiver) {
47 im.receivers = append(im.receivers, r)
48}
49
50// ReceiveEvents starts receiving events from the registered input
51// receivers. It sends the events to the given event and error channels.
52func (im *InputManager) ReceiveEvents(ctx context.Context, events chan<- Event) error {
53 errg, ctx := errgroup.WithContext(ctx)
54 for _, r := range im.receivers {
55 errg.Go(func() error {
56 return r.ReceiveEvents(ctx, events)
57 })
58 }
59
60 // Wait for all receivers to finish
61 return errg.Wait()
62}
63
64// InitialSizeReceiver query the initial size of the terminal window and sends
65// it to the given event channel.
66type InitialSizeReceiver struct {
67 File term.File
68}
69
70// ReceiveEvents queries the initial size of the terminal window and sends it
71// to the given event channel. It stops when event is sent, the context is done
72// or an error occurs.
73func (r *InitialSizeReceiver) ReceiveEvents(ctx context.Context, events chan<- Event) error {
74 if r.File == nil {
75 return nil // No file set, nothing to do.
76 }
77
78 w, h, err := term.GetSize(r.File.Fd())
79 if err != nil {
80 return err
81 }
82
83 select {
84 case <-ctx.Done():
85 return ctx.Err()
86 case events <- WindowSizeEvent{Width: w, Height: h}:
87 return nil
88 }
89}