github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/app/internal/input/router.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package input 4 5 import ( 6 "encoding/binary" 7 "time" 8 9 "github.com/gop9/olt/gio/internal/opconst" 10 "github.com/gop9/olt/gio/internal/ops" 11 "github.com/gop9/olt/gio/io/event" 12 "github.com/gop9/olt/gio/io/key" 13 "github.com/gop9/olt/gio/io/pointer" 14 "github.com/gop9/olt/gio/io/profile" 15 "github.com/gop9/olt/gio/op" 16 ) 17 18 // Router is a Queue implementation that routes events from 19 // all available input sources to registered handlers. 20 type Router struct { 21 pqueue pointerQueue 22 kqueue keyQueue 23 24 handlers handlerEvents 25 26 reader ops.Reader 27 28 // InvalidateOp summary. 29 wakeup bool 30 wakeupTime time.Time 31 32 // ProfileOp summary. 33 profiling bool 34 profHandlers map[event.Key]struct{} 35 profile profile.Event 36 } 37 38 type handlerEvents struct { 39 handlers map[event.Key][]event.Event 40 hadEvents bool 41 } 42 43 func (q *Router) Events(k event.Key) []event.Event { 44 events := q.handlers.Events(k) 45 if _, isprof := q.profHandlers[k]; isprof { 46 delete(q.profHandlers, k) 47 events = append(events, q.profile) 48 } 49 return events 50 } 51 52 func (q *Router) Frame(ops *op.Ops) { 53 q.handlers.Clear() 54 q.wakeup = false 55 q.profiling = false 56 for k := range q.profHandlers { 57 delete(q.profHandlers, k) 58 } 59 q.reader.Reset(ops) 60 q.collect() 61 62 q.pqueue.Frame(ops, &q.handlers) 63 q.kqueue.Frame(ops, &q.handlers) 64 if q.handlers.HadEvents() { 65 q.wakeup = true 66 q.wakeupTime = time.Time{} 67 } 68 } 69 70 func (q *Router) Add(e event.Event) bool { 71 switch e := e.(type) { 72 case pointer.Event: 73 q.pqueue.Push(e, &q.handlers) 74 case key.EditEvent, key.Event, key.FocusEvent: 75 q.kqueue.Push(e, &q.handlers) 76 } 77 return q.handlers.HadEvents() 78 } 79 80 func (q *Router) TextInputState() TextInputState { 81 return q.kqueue.InputState() 82 } 83 84 func (q *Router) collect() { 85 for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { 86 switch opconst.OpType(encOp.Data[0]) { 87 case opconst.TypeInvalidate: 88 op := decodeInvalidateOp(encOp.Data) 89 if !q.wakeup || op.At.Before(q.wakeupTime) { 90 q.wakeup = true 91 q.wakeupTime = op.At 92 } 93 case opconst.TypeProfile: 94 op := decodeProfileOp(encOp.Data, encOp.Refs) 95 if q.profHandlers == nil { 96 q.profHandlers = make(map[event.Key]struct{}) 97 } 98 q.profiling = true 99 q.profHandlers[op.Key] = struct{}{} 100 } 101 } 102 } 103 104 func (q *Router) AddProfile(profile profile.Event) { 105 q.profile = profile 106 } 107 108 func (q *Router) Profiling() bool { 109 return q.profiling 110 } 111 112 func (q *Router) WakeupTime() (time.Time, bool) { 113 return q.wakeupTime, q.wakeup 114 } 115 116 func (h *handlerEvents) init() { 117 if h.handlers == nil { 118 h.handlers = make(map[event.Key][]event.Event) 119 } 120 } 121 122 func (h *handlerEvents) Set(k event.Key, evts []event.Event) { 123 h.init() 124 h.handlers[k] = evts 125 h.hadEvents = true 126 } 127 128 func (h *handlerEvents) Add(k event.Key, e event.Event) { 129 h.init() 130 h.handlers[k] = append(h.handlers[k], e) 131 h.hadEvents = true 132 } 133 134 func (h *handlerEvents) HadEvents() bool { 135 u := h.hadEvents 136 h.hadEvents = false 137 return u 138 } 139 140 func (h *handlerEvents) Events(k event.Key) []event.Event { 141 if events, ok := h.handlers[k]; ok { 142 h.handlers[k] = h.handlers[k][:0] 143 // Schedule another frame if we delivered events to the user 144 // to flush half-updated state. This is important when a an 145 // event changes UI state that has already been laid out. In 146 // the worst case, we waste a frame, increasing power usage. 147 // 148 // Gio is expected to grow the ability to construct 149 // frame-to-frame differences and only render to changed 150 // areas. In that case, the waste of a spurious frame should 151 // be minimal. 152 h.hadEvents = h.hadEvents || len(events) > 0 153 return events 154 } 155 return nil 156 } 157 158 func (h *handlerEvents) Clear() { 159 for k := range h.handlers { 160 delete(h.handlers, k) 161 } 162 } 163 164 func decodeProfileOp(d []byte, refs []interface{}) profile.Op { 165 if opconst.OpType(d[0]) != opconst.TypeProfile { 166 panic("invalid op") 167 } 168 return profile.Op{ 169 Key: refs[0].(event.Key), 170 } 171 } 172 173 func decodeInvalidateOp(d []byte) op.InvalidateOp { 174 bo := binary.LittleEndian 175 if opconst.OpType(d[0]) != opconst.TypeInvalidate { 176 panic("invalid op") 177 } 178 var o op.InvalidateOp 179 if nanos := bo.Uint64(d[1:]); nanos > 0 { 180 o.At = time.Unix(0, int64(nanos)) 181 } 182 return o 183 }