package draw9 import ( "fmt" "image" "log" "os" "syscall" ) type Cursor struct { Offset image.Point Clear [2 * 16]byte Set [2 * 16]byte } type Menu struct { // ??? } // Mouse is a record of one event from /dev/mouse. type Mouse struct { buttons int xy image.Point msec uint } func (m Mouse) String() string { out := "" switch m.buttons { case 1: out += "Mb1" case 2: out += "Mb2" case 4: out += "Mb3" case 8: out += "SUp" case 16: out += "SDn" default: out += "nil" } out += fmt.Sprintf(" %s %d", m.xy, m.msec) return out } // Mb1 returns true if the left mouse button was pressed. func (m *Mouse) Mb1() bool { return m.buttons == 1 } // Mb2 returns true if the middle mouse button was pressed. func (m *Mouse) Mb2() bool { return m.buttons == 2 } // Mb3 returns true if the right mouse button was pressed. func (m *Mouse) Mb3() bool { return m.buttons == 4 } // Any returns true if any mouse button is down. func (m *Mouse) Any() bool { return m.buttons & 7 > 0 } func (m *Mouse) Buttons() int { return m.buttons } // ScrollUp returns true if the scroll wheel went up. func (m *Mouse) ScrollUp() bool { return m.buttons == 8 } // ScrollDown returns true if the scroll wheel went down. func (m *Mouse) ScrollDown() bool { return m.buttons == 16 } // Pt returns the point on the window at which the event happened. func (m *Mouse) Pt() image.Point { return m.xy } // Ms returns the millisecond timestamp of the Mouse event. func (m *Mouse) Ms() uint { return m.msec } type Mousectl struct { Mouse C chan Mouse Resize chan int quit chan bool // /dev/mouse mfd *os.File // /dev/cursor cfd *os.File // window/display image *Image } func InitMouse(file string, img *Image) *Mousectl { var err error var ms Mousectl if file == "" { file = "/dev/mouse" } ms.mfd, err = os.OpenFile(file, os.O_RDWR|syscall.O_CLOEXEC, 0666) if err != nil && file == "/dev/mouse" { syscall.Bind("#m", "/dev", syscall.MAFTER) ms.mfd, err = os.OpenFile(file, os.O_RDWR|syscall.O_CLOEXEC, 0666) } if err != nil { log.Fatal(err) } ms.cfd, err = os.OpenFile("/dev/cursor", os.O_RDWR|syscall.O_CLOEXEC, 0666) ms.image = img ms.C = make(chan Mouse) ms.Resize = make(chan int, 2) ms.quit = make(chan bool, 1) go ms.readproc() return &ms } func (ms *Mousectl) SetCursor(c *Cursor) { if c == nil { ms.cfd.Write([]byte{0}) } else { buf := make([]byte, 2*4+2*2*16) bplong(buf, uint32(c.Offset.X)) bplong(buf[4:], uint32(c.Offset.Y)) copy(buf[8:], c.Clear[:]) copy(buf[8+2*16:], c.Set[:]) ms.cfd.Write(buf) } } func (ms *Mousectl) MoveTo(pt image.Point) { fmt.Fprintf(ms.mfd, "m%d %d", pt.X, pt.Y) ms.xy = pt } func (ms *Mousectl) Close() { close(ms.quit) ms.mfd.Close() ms.cfd.Close() } func (ms *Mousectl) ReadMouse() Mouse { if ms.image != nil { ms.image.Display.flush(true) } return <-ms.C } func (ms *Mousectl) readproc() { var m Mouse buf := make([]byte, 1+5*12) loop: for { select { case <-ms.quit: break loop default: n, err := ms.mfd.Read(buf) if n != 1+4*12 { log.Fatalf("mouse: bad count %d: %s", n, err) } switch buf[0] { // resize case 'r': ms.Resize <- 1 fallthrough // mouse move case 'm': m.xy.X = atoi(buf[1+0*12:]) m.xy.Y = atoi(buf[1+1*12:]) m.buttons = atoi(buf[1+2*12:]) m.msec = uint(atoi(buf[1+3*12:])) ms.C <- m } } } close(ms.C) close(ms.Resize) } /* TODO: setcursor */