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  }