github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/app/internal/input/pointer.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package input 4 5 import ( 6 "encoding/binary" 7 "image" 8 9 "github.com/gop9/olt/gio/f32" 10 "github.com/gop9/olt/gio/internal/opconst" 11 "github.com/gop9/olt/gio/internal/ops" 12 "github.com/gop9/olt/gio/io/event" 13 "github.com/gop9/olt/gio/io/pointer" 14 "github.com/gop9/olt/gio/op" 15 ) 16 17 type pointerQueue struct { 18 hitTree []hitNode 19 areas []areaNode 20 handlers map[event.Key]*pointerHandler 21 pointers []pointerInfo 22 reader ops.Reader 23 scratch []event.Key 24 } 25 26 type hitNode struct { 27 next int 28 area int 29 // Pass tracks the most recent PassOp mode. 30 pass bool 31 32 // For handler nodes. 33 key event.Key 34 } 35 36 type pointerInfo struct { 37 id pointer.ID 38 pressed bool 39 handlers []event.Key 40 } 41 42 type pointerHandler struct { 43 area int 44 active bool 45 transform op.TransformOp 46 wantsGrab bool 47 } 48 49 type areaOp struct { 50 kind areaKind 51 rect image.Rectangle 52 } 53 54 type areaNode struct { 55 trans op.TransformOp 56 next int 57 area areaOp 58 } 59 60 type areaKind uint8 61 62 const ( 63 areaRect areaKind = iota 64 areaEllipse 65 ) 66 67 func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t op.TransformOp, area, node int, pass bool) { 68 for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { 69 switch opconst.OpType(encOp.Data[0]) { 70 case opconst.TypePush: 71 q.collectHandlers(r, events, t, area, node, pass) 72 case opconst.TypePop: 73 return 74 case opconst.TypePass: 75 op := decodePassOp(encOp.Data) 76 pass = op.Pass 77 case opconst.TypeArea: 78 var op areaOp 79 op.Decode(encOp.Data) 80 q.areas = append(q.areas, areaNode{trans: t, next: area, area: op}) 81 area = len(q.areas) - 1 82 q.hitTree = append(q.hitTree, hitNode{ 83 next: node, 84 area: area, 85 pass: pass, 86 }) 87 node = len(q.hitTree) - 1 88 case opconst.TypeTransform: 89 dop := ops.DecodeTransformOp(encOp.Data) 90 t = t.Multiply(op.TransformOp(dop)) 91 case opconst.TypePointerInput: 92 op := decodePointerInputOp(encOp.Data, encOp.Refs) 93 q.hitTree = append(q.hitTree, hitNode{ 94 next: node, 95 area: area, 96 pass: pass, 97 key: op.Key, 98 }) 99 node = len(q.hitTree) - 1 100 h, ok := q.handlers[op.Key] 101 if !ok { 102 h = new(pointerHandler) 103 q.handlers[op.Key] = h 104 events.Set(op.Key, []event.Event{pointer.Event{Type: pointer.Cancel}}) 105 } 106 h.active = true 107 h.area = area 108 h.transform = t 109 h.wantsGrab = h.wantsGrab || op.Grab 110 } 111 } 112 } 113 114 func (q *pointerQueue) opHit(handlers *[]event.Key, pos f32.Point) { 115 // Track whether we're passing through hits. 116 pass := true 117 idx := len(q.hitTree) - 1 118 for idx >= 0 { 119 n := &q.hitTree[idx] 120 if !q.hit(n.area, pos) { 121 idx-- 122 continue 123 } 124 pass = pass && n.pass 125 if pass { 126 idx-- 127 } else { 128 idx = n.next 129 } 130 if n.key != nil { 131 if _, exists := q.handlers[n.key]; exists { 132 *handlers = append(*handlers, n.key) 133 } 134 135 } 136 } 137 } 138 139 func (q *pointerQueue) hit(areaIdx int, p f32.Point) bool { 140 for areaIdx != -1 { 141 a := &q.areas[areaIdx] 142 if !a.hit(p) { 143 return false 144 } 145 areaIdx = a.next 146 } 147 return true 148 } 149 150 func (a *areaNode) hit(p f32.Point) bool { 151 p = a.trans.Invert().Transform(p) 152 return a.area.Hit(p) 153 } 154 155 func (q *pointerQueue) init() { 156 if q.handlers == nil { 157 q.handlers = make(map[event.Key]*pointerHandler) 158 } 159 } 160 161 func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) { 162 q.init() 163 for _, h := range q.handlers { 164 // Reset handler. 165 h.active = false 166 } 167 q.hitTree = q.hitTree[:0] 168 q.areas = q.areas[:0] 169 q.reader.Reset(root) 170 q.collectHandlers(&q.reader, events, op.TransformOp{}, -1, -1, false) 171 for k, h := range q.handlers { 172 if !h.active { 173 q.dropHandler(k, events) 174 delete(q.handlers, k) 175 } 176 } 177 } 178 179 func (q *pointerQueue) dropHandler(k event.Key, events *handlerEvents) { 180 events.Add(k, pointer.Event{Type: pointer.Cancel}) 181 q.handlers[k].wantsGrab = false 182 for i := range q.pointers { 183 p := &q.pointers[i] 184 for i := len(p.handlers) - 1; i >= 0; i-- { 185 if p.handlers[i] == k { 186 p.handlers = append(p.handlers[:i], p.handlers[i+1:]...) 187 } 188 } 189 } 190 } 191 192 func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) { 193 q.init() 194 if e.Type == pointer.Cancel { 195 q.pointers = q.pointers[:0] 196 for k := range q.handlers { 197 q.dropHandler(k, events) 198 } 199 return 200 } 201 pidx := -1 202 for i, p := range q.pointers { 203 if p.id == e.PointerID { 204 pidx = i 205 break 206 } 207 } 208 if pidx == -1 { 209 q.pointers = append(q.pointers, pointerInfo{id: e.PointerID}) 210 pidx = len(q.pointers) - 1 211 } 212 p := &q.pointers[pidx] 213 if !p.pressed && (e.Type == pointer.Move || e.Type == pointer.Press) { 214 p.handlers, q.scratch = q.scratch[:0], p.handlers 215 q.opHit(&p.handlers, e.Position) 216 if e.Type == pointer.Press { 217 p.pressed = true 218 } 219 } 220 if p.pressed { 221 // Resolve grabs. 222 q.scratch = q.scratch[:0] 223 for i, k := range p.handlers { 224 h := q.handlers[k] 225 if h.wantsGrab { 226 q.scratch = append(q.scratch, p.handlers[:i]...) 227 q.scratch = append(q.scratch, p.handlers[i+1:]...) 228 break 229 } 230 } 231 // Drop handlers that lost their grab. 232 for _, k := range q.scratch { 233 q.dropHandler(k, events) 234 } 235 } 236 if e.Type == pointer.Release { 237 q.pointers = append(q.pointers[:pidx], q.pointers[pidx+1:]...) 238 } 239 for _, k := range p.handlers { 240 h := q.handlers[k] 241 e := e 242 if p.pressed && len(p.handlers) == 1 { 243 e.Priority = pointer.Grabbed 244 } 245 e.Hit = q.hit(h.area, e.Position) 246 e.Position = h.transform.Invert().Transform(e.Position) 247 events.Add(k, e) 248 if e.Type == pointer.Release { 249 // Release grab when the number of grabs reaches zero. 250 grabs := 0 251 for _, p := range q.pointers { 252 if p.pressed && len(p.handlers) == 1 && p.handlers[0] == k { 253 grabs++ 254 } 255 } 256 if grabs == 0 { 257 h.wantsGrab = false 258 } 259 } 260 } 261 } 262 263 func (op *areaOp) Decode(d []byte) { 264 if opconst.OpType(d[0]) != opconst.TypeArea { 265 panic("invalid op") 266 } 267 bo := binary.LittleEndian 268 rect := image.Rectangle{ 269 Min: image.Point{ 270 X: int(int32(bo.Uint32(d[2:]))), 271 Y: int(int32(bo.Uint32(d[6:]))), 272 }, 273 Max: image.Point{ 274 X: int(int32(bo.Uint32(d[10:]))), 275 Y: int(int32(bo.Uint32(d[14:]))), 276 }, 277 } 278 *op = areaOp{ 279 kind: areaKind(d[1]), 280 rect: rect, 281 } 282 } 283 284 func (op *areaOp) Hit(pos f32.Point) bool { 285 min := f32.Point{ 286 X: float32(op.rect.Min.X), 287 Y: float32(op.rect.Min.Y), 288 } 289 pos = pos.Sub(min) 290 size := op.rect.Size() 291 switch op.kind { 292 case areaRect: 293 if 0 <= pos.X && pos.X < float32(size.X) && 294 0 <= pos.Y && pos.Y < float32(size.Y) { 295 return true 296 } else { 297 return false 298 } 299 case areaEllipse: 300 rx := float32(size.X) / 2 301 ry := float32(size.Y) / 2 302 rx2 := rx * rx 303 ry2 := ry * ry 304 xh := pos.X - rx 305 yk := pos.Y - ry 306 if xh*xh*ry2+yk*yk*rx2 <= rx2*ry2 { 307 return true 308 } else { 309 return false 310 } 311 default: 312 panic("invalid area kind") 313 } 314 } 315 316 func decodePointerInputOp(d []byte, refs []interface{}) pointer.InputOp { 317 if opconst.OpType(d[0]) != opconst.TypePointerInput { 318 panic("invalid op") 319 } 320 return pointer.InputOp{ 321 Grab: d[1] != 0, 322 Key: refs[0].(event.Key), 323 } 324 } 325 326 func decodePassOp(d []byte) pointer.PassOp { 327 if opconst.OpType(d[0]) != opconst.TypePass { 328 panic("invalid op") 329 } 330 return pointer.PassOp{ 331 Pass: d[1] != 0, 332 } 333 }