File: mouse.go

package info (click to toggle)
golang-github-charmbracelet-x 0.0~git20251028.0cf22f8%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,940 kB
  • sloc: sh: 124; makefile: 5
file content (172 lines) | stat: -rw-r--r-- 4,365 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package ansi

import (
	"fmt"
)

// MouseButton represents the button that was pressed during a mouse message.
type MouseButton byte

// Mouse event buttons
//
// This is based on X11 mouse button codes.
//
//	1 = left button
//	2 = middle button (pressing the scroll wheel)
//	3 = right button
//	4 = turn scroll wheel up
//	5 = turn scroll wheel down
//	6 = push scroll wheel left
//	7 = push scroll wheel right
//	8 = 4th button (aka browser backward button)
//	9 = 5th button (aka browser forward button)
//	10
//	11
//
// Other buttons are not supported.
const (
	MouseNone MouseButton = iota
	MouseButton1
	MouseButton2
	MouseButton3
	MouseButton4
	MouseButton5
	MouseButton6
	MouseButton7
	MouseButton8
	MouseButton9
	MouseButton10
	MouseButton11

	MouseLeft       = MouseButton1
	MouseMiddle     = MouseButton2
	MouseRight      = MouseButton3
	MouseWheelUp    = MouseButton4
	MouseWheelDown  = MouseButton5
	MouseWheelLeft  = MouseButton6
	MouseWheelRight = MouseButton7
	MouseBackward   = MouseButton8
	MouseForward    = MouseButton9
	MouseRelease    = MouseNone
)

var mouseButtons = map[MouseButton]string{
	MouseNone:       "none",
	MouseLeft:       "left",
	MouseMiddle:     "middle",
	MouseRight:      "right",
	MouseWheelUp:    "wheelup",
	MouseWheelDown:  "wheeldown",
	MouseWheelLeft:  "wheelleft",
	MouseWheelRight: "wheelright",
	MouseBackward:   "backward",
	MouseForward:    "forward",
	MouseButton10:   "button10",
	MouseButton11:   "button11",
}

// String returns a string representation of the mouse button.
func (b MouseButton) String() string {
	return mouseButtons[b]
}

// EncodeMouseButton returns a byte representing a mouse button.
// The button is a bitmask of the following leftmost values:
//
//   - The first two bits are the button number:
//     0 = left button, wheel up, or button no. 8 aka (backwards)
//     1 = middle button, wheel down, or button no. 9 aka (forwards)
//     2 = right button, wheel left, or button no. 10
//     3 = release event, wheel right, or button no. 11
//
//   - The third bit indicates whether the shift key was pressed.
//
//   - The fourth bit indicates the alt key was pressed.
//
//   - The fifth bit indicates the control key was pressed.
//
//   - The sixth bit indicates motion events. Combined with button number 3, i.e.
//     release event, it represents a drag event.
//
//   - The seventh bit indicates a wheel event.
//
//   - The eighth bit indicates additional buttons.
//
// If button is [MouseNone], and motion is false, this returns a release event.
// If button is undefined, this function returns 0xff.
func EncodeMouseButton(b MouseButton, motion, shift, alt, ctrl bool) (m byte) {
	// mouse bit shifts
	const (
		bitShift  = 0b0000_0100
		bitAlt    = 0b0000_1000
		bitCtrl   = 0b0001_0000
		bitMotion = 0b0010_0000
		bitWheel  = 0b0100_0000
		bitAdd    = 0b1000_0000 // additional buttons 8-11

		bitsMask = 0b0000_0011
	)

	if b == MouseNone {
		m = bitsMask
	} else if b >= MouseLeft && b <= MouseRight {
		m = byte(b - MouseLeft)
	} else if b >= MouseWheelUp && b <= MouseWheelRight {
		m = byte(b - MouseWheelUp)
		m |= bitWheel
	} else if b >= MouseBackward && b <= MouseButton11 {
		m = byte(b - MouseBackward)
		m |= bitAdd
	} else {
		m = 0xff // invalid button
	}

	if shift {
		m |= bitShift
	}
	if alt {
		m |= bitAlt
	}
	if ctrl {
		m |= bitCtrl
	}
	if motion {
		m |= bitMotion
	}

	return m
}

// x10Offset is the offset for X10 mouse events.
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
const x10Offset = 32

// MouseX10 returns an escape sequence representing a mouse event in X10 mode.
// Note that this requires the terminal support X10 mouse modes.
//
//	CSI M Cb Cx Cy
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
func MouseX10(b byte, x, y int) string {
	return "\x1b[M" + string(b+x10Offset) + string(byte(x)+x10Offset+1) + string(byte(y)+x10Offset+1)
}

// MouseSgr returns an escape sequence representing a mouse event in SGR mode.
//
//	CSI < Cb ; Cx ; Cy M
//	CSI < Cb ; Cx ; Cy m (release)
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
func MouseSgr(b byte, x, y int, release bool) string {
	s := 'M'
	if release {
		s = 'm'
	}
	if x < 0 {
		x = -x
	}
	if y < 0 {
		y = -y
	}
	return fmt.Sprintf("\x1b[<%d;%d;%d%c", b, x+1, y+1, s)
}