1 // SPDX-License-Identifier: Unlicense OR MIT
2 3 package pointer
4 5 import (
6 "encoding/binary"
7 "fmt"
8 "image"
9 "strings"
10 "time"
11 12 "github.com/p9c/p9/pkg/gel/gio/f32"
13 "github.com/p9c/p9/pkg/gel/gio/internal/opconst"
14 "github.com/p9c/p9/pkg/gel/gio/io/event"
15 "github.com/p9c/p9/pkg/gel/gio/io/key"
16 "github.com/p9c/p9/pkg/gel/gio/op"
17 )
18 19 // Event is a pointer event.
20 type Event struct {
21 Type Type
22 Source Source
23 // PointerID is the id for the pointer and can be used
24 // to track a particular pointer from Press to
25 // Release or Cancel.
26 PointerID ID
27 // Priority is the priority of the receiving handler
28 // for this event.
29 Priority Priority
30 // Time is when the event was received. The
31 // timestamp is relative to an undefined base.
32 Time time.Duration
33 // Buttons are the set of pressed mouse buttons for this event.
34 Buttons Buttons
35 // Position is the position of the event, relative to
36 // the current transformation, as set by op.TransformOp.
37 Position f32.Point
38 // Scroll is the scroll amount, if any.
39 Scroll f32.Point
40 // Modifiers is the set of active modifiers when
41 // the mouse button was pressed.
42 Modifiers key.Modifiers
43 }
44 45 // AreaOp updates the hit area to the intersection of the current
46 // hit area and the area. The area is transformed before applying
47 // it.
48 type AreaOp struct {
49 kind areaKind
50 rect image.Rectangle
51 }
52 53 // CursorNameOp sets the cursor for the current area.
54 type CursorNameOp struct {
55 Name CursorName
56 }
57 58 // InputOp declares an input handler ready for pointer
59 // events.
60 type InputOp struct {
61 Tag event.Tag
62 // Grab, if set, request that the handler get
63 // Grabbed priority.
64 Grab bool
65 // Types is a bitwise-or of event types to receive.
66 Types Type
67 // ScrollBounds describe the maximum scrollable distances in both
68 // axes. Specifically, any Event e delivered to Tag will satisfy
69 //
70 // ScrollBounds.Min.X <= e.Scroll.X <= ScrollBounds.Max.X (horizontal axis)
71 // ScrollBounds.Min.Y <= e.Scroll.Y <= ScrollBounds.Max.Y (vertical axis)
72 ScrollBounds image.Rectangle
73 }
74 75 // PassOp sets the pass-through mode.
76 type PassOp struct {
77 Pass bool
78 }
79 80 type ID uint16
81 82 // Type of an Event.
83 type Type uint8
84 85 // Priority of an Event.
86 type Priority uint8
87 88 // Source of an Event.
89 type Source uint8
90 91 // Buttons is a set of mouse buttons
92 type Buttons uint8
93 94 // CursorName is the name of a cursor.
95 type CursorName string
96 97 // Must match app/internal/input.areaKind
98 type areaKind uint8
99 100 const (
101 // CursorDefault is the default cursor.
102 CursorDefault CursorName = ""
103 // CursorText is the cursor for text.
104 CursorText CursorName = "text"
105 // CursorPointer is the cursor for a link.
106 CursorPointer CursorName = "pointer"
107 // CursorCrossHair is the cursor for precise location.
108 CursorCrossHair CursorName = "crosshair"
109 // CursorColResize is the cursor for vertical resize.
110 CursorColResize CursorName = "col-resize"
111 // CursorRowResize is the cursor for horizontal resize.
112 CursorRowResize CursorName = "row-resize"
113 // CursorGrab is the cursor for moving object in any direction.
114 CursorGrab CursorName = "grab"
115 // CursorNone hides the cursor. To show it again, use any other cursor.
116 CursorNone CursorName = "none"
117 )
118 119 const (
120 // A Cancel event is generated when the current gesture is
121 // interrupted by other handlers or the system.
122 Cancel Type = (1 << iota) >> 1
123 // Press of a pointer.
124 Press
125 // Release of a pointer.
126 Release
127 // Move of a pointer.
128 Move
129 // Drag of a pointer.
130 Drag
131 // Pointer enters an area watching for pointer input
132 Enter
133 // Pointer leaves an area watching for pointer input
134 Leave
135 // Scroll of a pointer.
136 Scroll
137 )
138 139 const (
140 // Mouse generated event.
141 Mouse Source = iota
142 // Touch generated event.
143 Touch
144 )
145 146 const (
147 // Shared priority is for handlers that
148 // are part of a matching set larger than 1.
149 Shared Priority = iota
150 // Foremost priority is like Shared, but the
151 // handler is the foremost of the matching set.
152 Foremost
153 // Grabbed is used for matching sets of size 1.
154 Grabbed
155 )
156 157 const (
158 // ButtonPrimary is the primary button, usually the left button for a
159 // right-handed user.
160 ButtonPrimary Buttons = 1 << iota
161 // ButtonSecondary is the secondary button, usually the right button for a
162 // right-handed user.
163 ButtonSecondary
164 // ButtonTertiary is the tertiary button, usually the middle button.
165 ButtonTertiary
166 )
167 168 const (
169 areaRect areaKind = iota
170 areaEllipse
171 )
172 173 // Rect constructs a rectangular hit area.
174 func Rect(size image.Rectangle) AreaOp {
175 return AreaOp{
176 kind: areaRect,
177 rect: size,
178 }
179 }
180 181 // Ellipse constructs an ellipsoid hit area.
182 func Ellipse(size image.Rectangle) AreaOp {
183 return AreaOp{
184 kind: areaEllipse,
185 rect: size,
186 }
187 }
188 189 func (op AreaOp) Add(o *op.Ops) {
190 data := o.Write(opconst.TypeAreaLen)
191 data[0] = byte(opconst.TypeArea)
192 data[1] = byte(op.kind)
193 bo := binary.LittleEndian
194 bo.PutUint32(data[2:], uint32(op.rect.Min.X))
195 bo.PutUint32(data[6:], uint32(op.rect.Min.Y))
196 bo.PutUint32(data[10:], uint32(op.rect.Max.X))
197 bo.PutUint32(data[14:], uint32(op.rect.Max.Y))
198 }
199 200 func (op CursorNameOp) Add(o *op.Ops) {
201 data := o.Write1(opconst.TypeCursorLen, op.Name)
202 data[0] = byte(opconst.TypeCursor)
203 }
204 205 // Add panics if the scroll range does not contain zero.
206 func (op InputOp) Add(o *op.Ops) {
207 if op.Tag == nil {
208 panic("Tag must be non-nil")
209 }
210 if b := op.ScrollBounds; b.Min.X > 0 || b.Max.X < 0 || b.Min.Y > 0 || b.Max.Y < 0 {
211 panic(fmt.Errorf("invalid scroll range value %v", b))
212 }
213 data := o.Write1(opconst.TypePointerInputLen, op.Tag)
214 data[0] = byte(opconst.TypePointerInput)
215 if op.Grab {
216 data[1] = 1
217 }
218 data[2] = byte(op.Types)
219 bo := binary.LittleEndian
220 bo.PutUint32(data[3:], uint32(op.ScrollBounds.Min.X))
221 bo.PutUint32(data[7:], uint32(op.ScrollBounds.Min.Y))
222 bo.PutUint32(data[11:], uint32(op.ScrollBounds.Max.X))
223 bo.PutUint32(data[15:], uint32(op.ScrollBounds.Max.Y))
224 }
225 226 func (op PassOp) Add(o *op.Ops) {
227 data := o.Write(opconst.TypePassLen)
228 data[0] = byte(opconst.TypePass)
229 if op.Pass {
230 data[1] = 1
231 }
232 }
233 234 func (t Type) String() string {
235 switch t {
236 case Press:
237 return "Press"
238 case Release:
239 return "Release"
240 case Cancel:
241 return "Cancel"
242 case Move:
243 return "Move"
244 case Drag:
245 return "Drag"
246 case Enter:
247 return "Enter"
248 case Leave:
249 return "Leave"
250 case Scroll:
251 return "Scroll"
252 default:
253 panic("unknown Type")
254 }
255 }
256 257 func (p Priority) String() string {
258 switch p {
259 case Shared:
260 return "Shared"
261 case Foremost:
262 return "Foremost"
263 case Grabbed:
264 return "Grabbed"
265 default:
266 panic("unknown priority")
267 }
268 }
269 270 func (s Source) String() string {
271 switch s {
272 case Mouse:
273 return "Mouse"
274 case Touch:
275 return "Touch"
276 default:
277 panic("unknown source")
278 }
279 }
280 281 // Contain reports whether the set b contains
282 // all of the buttons.
283 func (b Buttons) Contain(buttons Buttons) bool {
284 return b&buttons == buttons
285 }
286 287 func (b Buttons) String() string {
288 var strs []string
289 if b.Contain(ButtonPrimary) {
290 strs = append(strs, "ButtonPrimary")
291 }
292 if b.Contain(ButtonSecondary) {
293 strs = append(strs, "ButtonSecondary")
294 }
295 if b.Contain(ButtonTertiary) {
296 strs = append(strs, "ButtonTertiary")
297 }
298 return strings.Join(strs, "|")
299 }
300 301 func (c CursorName) String() string {
302 if c == CursorDefault {
303 return "default"
304 }
305 return string(c)
306 }
307 308 func (Event) ImplementsEvent() {}
309