ColorPing/main.go
2024-06-18 16:17:56 +03:00

187 lines
3.5 KiB
Go

package main
import (
"bytes"
"encoding/base64"
"encoding/binary"
"fmt"
"github.com/songgao/water"
"image"
"image/color"
"image/png"
"log"
"sync"
)
const interfaceName = "canvas"
const handlerCount = 4
func main() {
prePopulatePixelArray()
packetChan := make(chan *[]byte, 1000)
for i := 0; i < handlerCount; i++ {
go packetHandler(packetChan)
}
go startInterface(packetChan)
go streamServer()
fmt.Println("Kioubit ColorPing started")
fmt.Println("Interface name:", interfaceName, "HTTP server port: 9090")
httpServer()
}
func prePopulatePixelArray() {
for x := 0; x < len(pixelArray); x++ {
for y := 0; y < len(pixelArray[x]); y++ {
pixelArray[x][y] = &pixel{
r: uint8(0),
g: uint8(0),
b: uint8(0),
}
}
}
}
var pktPool = sync.Pool{
New: func() interface{} { return make([]byte, 2000) },
}
func startInterface(packetChan chan *[]byte) {
config := water.Config{
DeviceType: water.TUN,
}
config.Name = interfaceName
iFace, err := water.New(config)
if err != nil {
log.Fatal(err)
}
for {
packet := pktPool.Get().([]byte)
n, err := iFace.Read(packet)
if err != nil {
log.Fatal(err)
}
packet = packet[:n]
packetChan <- &packet
}
}
func packetHandler(packetChan chan *[]byte) {
for {
packet := <-packetChan
if len(*packet) < 40 {
continue
}
if (*packet)[0] != 0x60 {
continue
}
destinationAddress := (*packet)[24:40]
relevant := destinationAddress[8:]
// FORMAT: XXXX:YYYY:11RR:GGBB
x := binary.BigEndian.Uint16(relevant[0:2])
y := binary.BigEndian.Uint16(relevant[2:4])
if relevant[4] != 0x11 {
continue
}
r := relevant[5]
g := relevant[6]
b := relevant[7]
if x > 512 || y > 512 {
continue
}
obj := pixelArray[x][y]
obj.Lock()
if obj.r != r || obj.g != g || obj.b != b {
obj.r = r
obj.g = g
obj.b = b
obj.changed = true
}
obj.Unlock()
pktPool.Put(*packet)
}
}
type pixel struct {
sync.Mutex
r uint8
g uint8
b uint8
changed bool
}
// 0 - 513
var pixelArray [513][513]*pixel
func getPicture(fullUpdate bool, incrementalUpdate bool) (string, string) {
anyChange := false
canvasFullUpdate := image.NewRGBA(image.Rect(0, 0, 512, 512))
canvasIncrementalUpdate := image.NewRGBA(image.Rect(0, 0, 512, 512))
for x := 0; x < len(pixelArray); x++ {
for y := 0; y < len(pixelArray[x]); y++ {
obj := pixelArray[x][y]
obj.Lock()
var newColor *color.RGBA
if incrementalUpdate {
if obj.changed {
newColor = &color.RGBA{
R: obj.r,
G: obj.g,
B: obj.b,
A: 255,
}
obj.changed = false
anyChange = true
canvasIncrementalUpdate.SetRGBA(x, y, *newColor)
} else if !fullUpdate {
obj.Unlock()
continue
}
}
if newColor == nil {
newColor = &color.RGBA{
R: obj.r,
G: obj.g,
B: obj.b,
A: 255,
}
}
obj.Unlock()
canvasFullUpdate.SetRGBA(x, y, *newColor)
}
}
encoder := png.Encoder{
CompressionLevel: png.BestSpeed,
}
incrementalUpdateResult := "0"
if anyChange {
buff := new(bytes.Buffer)
err := encoder.Encode(buff, canvasIncrementalUpdate)
if err != nil {
log.Println(err.Error())
}
incrementalUpdateResult = "event: u\ndata:" + base64.StdEncoding.EncodeToString(buff.Bytes()) + "\n\n"
}
fullUpdateResult := "0"
if fullUpdate {
buff := new(bytes.Buffer)
err := encoder.Encode(buff, canvasFullUpdate)
if err != nil {
log.Println(err.Error())
}
fullUpdateResult = "event: u\ndata:" + base64.StdEncoding.EncodeToString(buff.Bytes()) + "\n\n"
}
return fullUpdateResult, incrementalUpdateResult
}