Skip to content
This repository has been archived by the owner on May 27, 2024. It is now read-only.

Commit

Permalink
add minitouch support
Browse files Browse the repository at this point in the history
  • Loading branch information
codeskyblue committed Dec 23, 2017
1 parent ee41a69 commit f198573
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 4 deletions.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,52 @@ $ curl -X PUT 10.0.0.1:7912/screenrecord
$ curl -X GET 10.0.0.1:7912/raw/sdcard/screenrecords/0.mp4
```

## Minitouch操作方法
感谢 [openstf/minitouch](https://github.com/openstf/minitouch)

Websocket连接 `$DEVICE_URL/minitouch`, 一行行的按照JSON的格式写入

>注: 坐标原点始终是手机正放时候的左上角,使用者需要自己处理旋转的变化
请先详细阅读minitouch的[Usage](https://github.com/openstf/minitouch#usage)文档,再来看下面的部分

- Touch Down

坐标(X: 50%, Y: 50%), index代表第几个手指, `pressure`是可选的。

```json
{"operation": "d", "index": 0, "pX": 0.5, "pY": 0.5, "pressure": 50}
```

- Touch Commit

```json
{"operation": "c"}
```

- Touch Move

```json
{"operation": "m", "index": 0, "pX": 0.5, "pY": 0.5, "pressure": 50}
```

- Touch Up

```json
{"operation": "u", "index": 0}
```

- 点击x:20%, y:20,滑动到x:40%, y:50%

```json
{"operation": "d", "index": 0, "pX": 0.20, "pY": 0.20, "pressure": 50}
{"operation": "c"}
{"operation": "m", "index": 0, "pX": 0.40, "pY": 0.50, "pressure": 50}
{"operation": "c"}
{"operation": "u", "index": 0}
{"operation": "c"}
```

# TODO
1. 目前安全性还是个问题,以后再想办法改善
2. 补全接口文档
Expand Down
4 changes: 4 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@

go generate
GOOS=linux GOARCH=arm go build -tags vfs
if test "$1" = "i"
then
cmd "/c install.bat"
fi
2 changes: 1 addition & 1 deletion install.bat
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ REM adb install app-debug-androidTest.apk

adb push atx-agent /data/local/tmp
adb shell chmod 777 /data/local/tmp/atx-agent
adb shell /data/local/tmp/atx-agent -d -t 10.240.187.224:8000 -nouia
REM adb shell /data/local/tmp/atx-agent -d -t 10.240.187.224:8000 -nouia
REM adb shell /data/local/tmp/atx-agent -d -t 10.246.46.160:8200 -nouia
REM pause
77 changes: 74 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import (
"context"
"encoding/binary"
"encoding/json"
"os/user"
"path/filepath"

"flag"
"fmt"
"html/template"
Expand All @@ -22,7 +19,9 @@ import (
"os"
"os/exec"
"os/signal"
"os/user"
"path"
"path/filepath"
"regexp"
"runtime"
"strconv"
Expand Down Expand Up @@ -63,6 +62,9 @@ func init() {
Environ: []string{"LD_LIBRARY_PATH=/data/local/tmp"},
Args: []string{"/data/local/tmp/minicap", "-S", "-P", fmt.Sprintf("%dx%d@800x800/0", width, height)},
})
service.Add("minitouch", cmdctrl.CommandInfo{
Args: []string{"/data/local/tmp/minitouch"},
})
}

// Get preferred outbound ip of this machine
Expand Down Expand Up @@ -763,6 +765,74 @@ func ServeHTTP(lis net.Listener, tunnel *TunnelProxy) error {
io.WriteString(w, version)
})

m.HandleFunc("/minitouch", func(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer ws.Close()
const wsWriteWait = 10 * time.Second
wsWrite := func(messageType int, data []byte) error {
ws.SetWriteDeadline(time.Now().Add(wsWriteWait))
return ws.WriteMessage(messageType, data)
}
wsWrite(websocket.TextMessage, []byte("start @minitouch service"))
if err := service.Start("minitouch"); err != nil && err != cmdctrl.ErrAlreadyRunning {
wsWrite(websocket.TextMessage, []byte("@minitouch service start failed: "+err.Error()))
return
}
wsWrite(websocket.TextMessage, []byte("dial unix:@minitouch"))
log.Printf("minitouch connection: %v", r.RemoteAddr)
retries := 0
quitC := make(chan bool, 2)
operC := make(chan TouchRequest, 10)
defer func() {
wsWrite(websocket.TextMessage, []byte("unix:@minitouch websocket closed"))
close(operC)
}()
go func() {
for {
if retries > 10 {
log.Println("unix @minitouch connect failed")
wsWrite(websocket.TextMessage, []byte("@minitouch listen timeout, possibly minitouch not installed"))
break
}
conn, err := net.Dial("unix", "@minitouch")
if err != nil {
retries++
log.Printf("dial @minitouch error: %v, wait 0.5s", err)
select {
case <-quitC:
return
case <-time.After(500 * time.Millisecond):
}
continue
}
log.Println("unix @minitouch connected, accepting requests")
retries = 0 // connected, reset retries
if err := drainTouchRequests(conn, operC); err != nil {
log.Println("drain touch requests err:", err)
}
conn.Close()
}
}()
var touchRequest TouchRequest
for {
err := ws.ReadJSON(&touchRequest)
if err != nil {
log.Println("readJson err:", err)
quitC <- true
}
select {
case operC <- touchRequest:
case <-time.After(2 * time.Second):
wsWrite(websocket.TextMessage, []byte("touch request buffer full"))
}
log.Printf("recieve: %v", touchRequest)
}
})

m.HandleFunc("/minicap", func(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
Expand Down Expand Up @@ -792,6 +862,7 @@ func ServeHTTP(lis net.Listener, tunnel *TunnelProxy) error {
retries := 0
for {
if retries > 10 {
log.Println("unix @minicap connect failed")
wsWrite(websocket.TextMessage, []byte("@minicap listen timeout, possibly minicap not installed"))
break
}
Expand Down
96 changes: 96 additions & 0 deletions minitouch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package main

import (
"bufio"
"errors"
"fmt"
"io"
"io/ioutil"
"net"

log "github.com/sirupsen/logrus"
)

type toucher struct {
width, height int
rotation int
}

type TouchRequest struct {
Operation string `json:"operation"` // d, m, u
Index int `json:"index"`
PercentX float64 `json:"pX"`
PercentY float64 `json:"pY"`
Pressure int `json:"pressure"`
}

// coord(0, 0) is always left-top conner, no matter the rotation changes
func drainTouchRequests(conn net.Conn, reqC chan TouchRequest) error {
var maxX, maxY int
var flag string
var ver int
var maxContacts, maxPressure int
var pid int

lineRd := lineFormatReader{bufrd: bufio.NewReader(conn)}
lineRd.Scanf("%s %d", &flag, &ver)
lineRd.Scanf("%s %d %d %d %d", &flag, &maxContacts, &maxX, &maxY, &maxPressure)
if err := lineRd.Scanf("%s %d", &flag, &pid); err != nil {
return err
}
log.WithFields(log.Fields{
"maxX": maxX,
"maxY": maxY,
"maxPressure": maxPressure,
"maxContacts": maxContacts,
}).Info("handle touch requests")
go io.Copy(ioutil.Discard, conn) // ignore the rest output
var posX, posY int
for req := range reqC {
var err error
switch req.Operation {
case "d":
fallthrough
case "m":
posX = int(req.PercentX * float64(maxX))
posY = int(req.PercentY * float64(maxY))
if req.Pressure == 0 {
req.Pressure = 50
}
line := fmt.Sprintf("%s %d %d %d %d\n", req.Operation, req.Index, posX, posY, req.Pressure)
log.WithFields(log.Fields{
"touch": req,
"remoteAddr": conn.RemoteAddr(),
}).Debug("write to @minitouch", line)
_, err = conn.Write([]byte(line))
case "u":
_, err = conn.Write([]byte(fmt.Sprintf("u %d\n", req.Index)))
case "c":
_, err = conn.Write([]byte("c\n"))
default:
err = errors.New("unsupported operation: " + req.Operation)
}
if err != nil {
return err
}
}
return nil
}

type lineFormatReader struct {
bufrd *bufio.Reader
err error
}

func (r *lineFormatReader) Scanf(format string, args ...interface{}) error {
if r.err != nil {
return r.err
}
var line []byte
line, _, r.err = r.bufrd.ReadLine()
if r.err != nil {
return r.err
}
_, r.err = fmt.Sscanf(string(line), format, args...)
return r.err
}
70 changes: 70 additions & 0 deletions minitouch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"bytes"
"net"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

type MockConn struct {
buffer *bytes.Buffer
}

func (c *MockConn) Read(b []byte) (n int, err error) {
return c.buffer.Read(b)
}

func (c *MockConn) Write(b []byte) (n int, err error) {
return c.buffer.Write(b)
}

func (c *MockConn) Close() error { return nil }
func (c *MockConn) LocalAddr() net.Addr { return nil }
func (c *MockConn) RemoteAddr() net.Addr { return nil }
func (c *MockConn) SetDeadline(t time.Time) error { return nil }
func (c *MockConn) SetReadDeadline(t time.Time) error { return nil }
func (c *MockConn) SetWriteDeadline(t time.Time) error { return nil }

func TestDrainTouchRequests(t *testing.T) {
reqC := make(chan TouchRequest, 0)
conn := &MockConn{
buffer: bytes.NewBuffer(nil),
}
err := drainTouchRequests(conn, reqC)
assert.Error(t, err)

conn = &MockConn{
buffer: bytes.NewBufferString(`v 1
^ 10 1080 1920 255
$ 25654`),
}
reqC = make(chan TouchRequest, 4)
reqC <- TouchRequest{
Operation: "d",
Index: 1,
PercentX: 1.0,
PercentY: 1.0,
Pressure: 10,
}
reqC <- TouchRequest{
Operation: "c",
}
reqC <- TouchRequest{
Operation: "m",
Index: 3,
PercentX: 0.5,
PercentY: 0.5,
Pressure: 30,
}
reqC <- TouchRequest{
Operation: "u",
Index: 4,
}
close(reqC)
drainTouchRequests(conn, reqC)
output := string(conn.buffer.Bytes())
assert.Equal(t, "d 1 1080 1920 10\nc\nm 3 540 960 30\nu 4\n", output)
}

0 comments on commit f198573

Please sign in to comment.