package device import ( "encoding/binary" "fmt" "time" ) // Touch immediately forwards an event on the persistent scrcpy connection. // It never launches an adb shell or waits for a complete gesture. func (s *Service) Touch(req TouchRequest) error { if req.Action < 0 || req.Action > 3 || req.Width < 1 || req.Height < 1 || req.Width > 65535 || req.Height > 65535 || req.X < 0 || req.Y < 0 || req.X >= req.Width || req.Y >= req.Height { return fmt.Errorf("触摸事件或画面坐标无效") } st := s.ensureStream() st.mu.Lock() defer st.mu.Unlock() if !st.running || st.serial != req.DeviceID || st.controlConn == nil { return fmt.Errorf("控制通道未连接,请重新开始预览") } if req.Action != 0 && !st.touchDown { return nil } if req.Action == 0 && st.touchDown { cancel := st.lastTouch cancel.Action = 3 if err := st.writeTouchLocked(cancel); err != nil { return err } } if err := st.writeTouchLocked(req); err != nil { return fmt.Errorf("发送触摸事件失败,请重新开始预览: %w", err) } st.lastTouch = req st.touchDown = req.Action == 0 || req.Action == 2 return nil } // scrcpy 5 INJECT_TOUCH_EVENT: 32 bytes, big endian. Generic finger id=-2. func encodeTouch(req TouchRequest) []byte { packet := make([]byte, 32) packet[0] = 2 packet[1] = byte(req.Action) binary.BigEndian.PutUint64(packet[2:10], ^uint64(1)) binary.BigEndian.PutUint32(packet[10:14], uint32(req.X)) binary.BigEndian.PutUint32(packet[14:18], uint32(req.Y)) binary.BigEndian.PutUint16(packet[18:20], uint16(req.Width)) binary.BigEndian.PutUint16(packet[20:22], uint16(req.Height)) if req.Action == 0 || req.Action == 2 { binary.BigEndian.PutUint16(packet[22:24], 65535) } return packet } func (st *previewStream) writeTouchLocked(req TouchRequest) error { return st.writeControlLocked(encodeTouch(req)) } func (st *previewStream) writeControlLocked(data []byte) error { _ = st.controlConn.SetWriteDeadline(time.Now().Add(500 * time.Millisecond)) for len(data) > 0 { n, err := st.controlConn.Write(data) if err != nil { return err } if n == 0 { return fmt.Errorf("控制通道写入中断") } data = data[n:] } return nil } // SetScreenOff changes physical display power while keeping mirroring active. func (s *Service) SetScreenOff(deviceID string, screenOff bool) error { st := s.ensureStream() st.lifecycle.Lock() defer st.lifecycle.Unlock() st.mu.Lock() defer st.mu.Unlock() if !st.running || st.serial != deviceID || st.controlConn == nil { return fmt.Errorf("控制通道未连接,请重新开始预览") } return st.setScreenOffLocked(screenOff) } func (st *previewStream) setScreenOffLocked(screenOff bool) error { if st.screenOff == screenOff { return nil } // scrcpy 5 SET_DISPLAY_POWER: type=10, boolean on (not Android sleep). on := byte(1) if screenOff { on = 0 } if err := st.writeControlLocked([]byte{10, on}); err != nil { return fmt.Errorf("调整手机屏幕失败,请重新开始预览: %w", err) } st.screenOff = screenOff return nil }