mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-02-01 11:34:59 +01:00
284 lines
6.6 KiB
Go
284 lines
6.6 KiB
Go
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
|
|
|
package loop
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var _ = fmt.Print
|
|
|
|
type MouseEventType uint
|
|
type MouseButtonFlag uint
|
|
|
|
const (
|
|
MOUSE_PRESS MouseEventType = iota
|
|
MOUSE_RELEASE
|
|
MOUSE_MOVE
|
|
MOUSE_CLICK
|
|
)
|
|
|
|
func (e MouseEventType) String() string {
|
|
switch e {
|
|
case MOUSE_PRESS:
|
|
return "press"
|
|
case MOUSE_RELEASE:
|
|
return "release"
|
|
case MOUSE_MOVE:
|
|
return "move"
|
|
case MOUSE_CLICK:
|
|
return "click"
|
|
}
|
|
return strconv.Itoa(int(e))
|
|
}
|
|
|
|
type PointerShape uint8
|
|
|
|
const (
|
|
// start pointer shape enum (auto generated by gen-key-constants.py do not edit)
|
|
DEFAULT_POINTER PointerShape = 0
|
|
TEXT_POINTER PointerShape = 1
|
|
POINTER_POINTER PointerShape = 2
|
|
HELP_POINTER PointerShape = 3
|
|
WAIT_POINTER PointerShape = 4
|
|
PROGRESS_POINTER PointerShape = 5
|
|
CROSSHAIR_POINTER PointerShape = 6
|
|
CELL_POINTER PointerShape = 7
|
|
VERTICAL_TEXT_POINTER PointerShape = 8
|
|
MOVE_POINTER PointerShape = 9
|
|
E_RESIZE_POINTER PointerShape = 10
|
|
NE_RESIZE_POINTER PointerShape = 11
|
|
NW_RESIZE_POINTER PointerShape = 12
|
|
N_RESIZE_POINTER PointerShape = 13
|
|
SE_RESIZE_POINTER PointerShape = 14
|
|
SW_RESIZE_POINTER PointerShape = 15
|
|
S_RESIZE_POINTER PointerShape = 16
|
|
W_RESIZE_POINTER PointerShape = 17
|
|
EW_RESIZE_POINTER PointerShape = 18
|
|
NS_RESIZE_POINTER PointerShape = 19
|
|
NESW_RESIZE_POINTER PointerShape = 20
|
|
NWSE_RESIZE_POINTER PointerShape = 21
|
|
ZOOM_IN_POINTER PointerShape = 22
|
|
ZOOM_OUT_POINTER PointerShape = 23
|
|
ALIAS_POINTER PointerShape = 24
|
|
COPY_POINTER PointerShape = 25
|
|
NOT_ALLOWED_POINTER PointerShape = 26
|
|
NO_DROP_POINTER PointerShape = 27
|
|
GRAB_POINTER PointerShape = 28
|
|
GRABBING_POINTER PointerShape = 29
|
|
|
|
// end pointer shape enum
|
|
)
|
|
|
|
func (e PointerShape) String() string {
|
|
switch e {
|
|
// start pointer shape tostring (auto generated by gen-key-constants.py do not edit)
|
|
case DEFAULT_POINTER:
|
|
return "default"
|
|
case TEXT_POINTER:
|
|
return "text"
|
|
case POINTER_POINTER:
|
|
return "pointer"
|
|
case HELP_POINTER:
|
|
return "help"
|
|
case WAIT_POINTER:
|
|
return "wait"
|
|
case PROGRESS_POINTER:
|
|
return "progress"
|
|
case CROSSHAIR_POINTER:
|
|
return "crosshair"
|
|
case CELL_POINTER:
|
|
return "cell"
|
|
case VERTICAL_TEXT_POINTER:
|
|
return "vertical-text"
|
|
case MOVE_POINTER:
|
|
return "move"
|
|
case E_RESIZE_POINTER:
|
|
return "e-resize"
|
|
case NE_RESIZE_POINTER:
|
|
return "ne-resize"
|
|
case NW_RESIZE_POINTER:
|
|
return "nw-resize"
|
|
case N_RESIZE_POINTER:
|
|
return "n-resize"
|
|
case SE_RESIZE_POINTER:
|
|
return "se-resize"
|
|
case SW_RESIZE_POINTER:
|
|
return "sw-resize"
|
|
case S_RESIZE_POINTER:
|
|
return "s-resize"
|
|
case W_RESIZE_POINTER:
|
|
return "w-resize"
|
|
case EW_RESIZE_POINTER:
|
|
return "ew-resize"
|
|
case NS_RESIZE_POINTER:
|
|
return "ns-resize"
|
|
case NESW_RESIZE_POINTER:
|
|
return "nesw-resize"
|
|
case NWSE_RESIZE_POINTER:
|
|
return "nwse-resize"
|
|
case ZOOM_IN_POINTER:
|
|
return "zoom-in"
|
|
case ZOOM_OUT_POINTER:
|
|
return "zoom-out"
|
|
case ALIAS_POINTER:
|
|
return "alias"
|
|
case COPY_POINTER:
|
|
return "copy"
|
|
case NOT_ALLOWED_POINTER:
|
|
return "not-allowed"
|
|
case NO_DROP_POINTER:
|
|
return "no-drop"
|
|
case GRAB_POINTER:
|
|
return "grab"
|
|
case GRABBING_POINTER:
|
|
return "grabbing"
|
|
// end pointer shape tostring
|
|
}
|
|
return strconv.Itoa(int(e))
|
|
}
|
|
|
|
const (
|
|
SHIFT_INDICATOR int = 1 << 2
|
|
ALT_INDICATOR = 1 << 3
|
|
CTRL_INDICATOR = 1 << 4
|
|
MOTION_INDICATOR = 1 << 5
|
|
)
|
|
|
|
const (
|
|
NO_MOUSE_BUTTON MouseButtonFlag = 0
|
|
LEFT_MOUSE_BUTTON MouseButtonFlag = 1 << iota
|
|
MIDDLE_MOUSE_BUTTON
|
|
RIGHT_MOUSE_BUTTON
|
|
FOURTH_MOUSE_BUTTON
|
|
FIFTH_MOUSE_BUTTON
|
|
SIXTH_MOUSE_BUTTON
|
|
SEVENTH_MOUSE_BUTTON
|
|
MOUSE_WHEEL_UP
|
|
MOUSE_WHEEL_DOWN
|
|
MOUSE_WHEEL_LEFT
|
|
MOUSE_WHEEL_RIGHT
|
|
)
|
|
|
|
var bmap = [...]MouseButtonFlag{LEFT_MOUSE_BUTTON, MIDDLE_MOUSE_BUTTON, RIGHT_MOUSE_BUTTON}
|
|
var ebmap = [...]MouseButtonFlag{FOURTH_MOUSE_BUTTON, FIFTH_MOUSE_BUTTON, SIXTH_MOUSE_BUTTON, SEVENTH_MOUSE_BUTTON}
|
|
var wbmap = [...]MouseButtonFlag{MOUSE_WHEEL_UP, MOUSE_WHEEL_DOWN, MOUSE_WHEEL_LEFT, MOUSE_WHEEL_RIGHT}
|
|
|
|
func (b MouseButtonFlag) String() string {
|
|
ans := ""
|
|
switch {
|
|
case b&LEFT_MOUSE_BUTTON != 0:
|
|
ans += "|LEFT"
|
|
case b&MIDDLE_MOUSE_BUTTON != 0:
|
|
ans += "|MIDDLE"
|
|
case b&RIGHT_MOUSE_BUTTON != 0:
|
|
ans += "|RIGHT"
|
|
case b&FOURTH_MOUSE_BUTTON != 0:
|
|
ans += "|FOURTH"
|
|
case b&FIFTH_MOUSE_BUTTON != 0:
|
|
ans += "|FIFTH"
|
|
case b&SIXTH_MOUSE_BUTTON != 0:
|
|
ans += "|SIXTH"
|
|
case b&SEVENTH_MOUSE_BUTTON != 0:
|
|
ans += "|SEVENTH"
|
|
case b&MOUSE_WHEEL_UP != 0:
|
|
ans += "|WHEEL_UP"
|
|
case b&MOUSE_WHEEL_DOWN != 0:
|
|
ans += "|WHEEL_DOWN"
|
|
case b&MOUSE_WHEEL_LEFT != 0:
|
|
ans += "|WHEEL_LEFT"
|
|
case b&MOUSE_WHEEL_RIGHT != 0:
|
|
ans += "|WHEEL_RIGHT"
|
|
}
|
|
ans = strings.TrimLeft(ans, "|")
|
|
if ans == "" {
|
|
ans = "NONE"
|
|
}
|
|
return ans
|
|
}
|
|
|
|
type MouseEvent struct {
|
|
Event_type MouseEventType
|
|
Buttons MouseButtonFlag
|
|
Mods KeyModifiers
|
|
Cell, Pixel struct{ X, Y int }
|
|
}
|
|
|
|
func (e MouseEvent) String() string {
|
|
return fmt.Sprintf("MouseEvent{%s %s %s Cell:%v Pixel:%v}", e.Event_type, e.Buttons, e.Mods, e.Cell, e.Pixel)
|
|
}
|
|
|
|
func pixel_to_cell(px, length, cell_length int) int {
|
|
px = max(0, min(px, length-1))
|
|
if cell_length > 0 {
|
|
return px / cell_length
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func decode_sgr_mouse(text string, screen_size ScreenSize) *MouseEvent {
|
|
last_letter := text[len(text)-1]
|
|
text = text[:len(text)-1]
|
|
parts := strings.Split(text, ";")
|
|
if len(parts) != 3 {
|
|
return nil
|
|
}
|
|
cb, err := strconv.Atoi(parts[0])
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
ans := MouseEvent{}
|
|
ans.Pixel.X, err = strconv.Atoi(parts[1])
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if len(parts[2]) < 1 {
|
|
return nil
|
|
}
|
|
if ans.Pixel.Y, err = strconv.Atoi(parts[2]); err != nil {
|
|
return nil
|
|
}
|
|
if last_letter == 'm' {
|
|
ans.Event_type = MOUSE_RELEASE
|
|
} else if cb&MOTION_INDICATOR != 0 {
|
|
ans.Event_type = MOUSE_MOVE
|
|
}
|
|
cb3 := cb & 3
|
|
if cb >= 128 {
|
|
ans.Buttons |= ebmap[cb3]
|
|
} else if cb >= 64 {
|
|
ans.Buttons |= wbmap[cb3]
|
|
} else if cb3 < 3 {
|
|
ans.Buttons |= bmap[cb3]
|
|
}
|
|
if cb&SHIFT_INDICATOR != 0 {
|
|
ans.Mods |= SHIFT
|
|
}
|
|
if cb&ALT_INDICATOR != 0 {
|
|
ans.Mods |= ALT
|
|
}
|
|
if cb&CTRL_INDICATOR != 0 {
|
|
ans.Mods |= CTRL
|
|
}
|
|
ans.Cell.X = pixel_to_cell(ans.Pixel.X, int(screen_size.WidthPx), int(screen_size.CellWidth))
|
|
ans.Cell.Y = pixel_to_cell(ans.Pixel.Y, int(screen_size.HeightPx), int(screen_size.CellHeight))
|
|
|
|
return &ans
|
|
}
|
|
|
|
func MouseEventFromCSI(csi string, screen_size ScreenSize) *MouseEvent {
|
|
if len(csi) == 0 {
|
|
return nil
|
|
}
|
|
last_char := csi[len(csi)-1]
|
|
if last_char != 'm' && last_char != 'M' {
|
|
return nil
|
|
}
|
|
if !strings.HasPrefix(csi, "<") {
|
|
return nil
|
|
}
|
|
return decode_sgr_mouse(csi[1:], screen_size)
|
|
}
|