// License: GPLv3 Copyright: 2023, Kovid Goyal, 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) }