Files
fancy-n/main.go
T
Vojtěch Káně e3602ac25e Handle multiple display connections
Also drop the `quit` channel as we get good-enough signaling by closing the data channel.
2026-10-05 10:16:48 +02:00

135 lines
3.1 KiB
Go

package main
import (
"encoding/json"
"io"
"log"
"net/http"
"os"
"sync"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
WriteBufferSize: 4096,
ReadBufferSize: 0,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type client struct {
nudges chan [2]int
quit chan bool
}
type clientsStore struct {
set map[*client]struct{}
sync.RWMutex
}
func main() {
var clients clientsStore
clients.set = make(map[*client]struct{})
go func() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Access-Control-Allow-Origin", "*")
w.Header().Add("Access-Control-Allow-Headers", "*")
w.Header().Add("Access-Control-Allow-Methods", "*")
switch r.Method {
case "OPTIONS":
w.WriteHeader(http.StatusNoContent)
return
case "POST":
var nudge [2]int
switch r.Header.Get("Content-Type") {
case "application/json":
var decoder = json.NewDecoder(r.Body)
if err := decoder.Decode(&nudge); err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
default:
w.WriteHeader(http.StatusBadRequest)
return
}
(func() {
clients.RLock()
defer clients.RUnlock()
for c, _ := range clients.set {
select {
case c.nudges <- nudge:
default:
log.Println("Client too slow -> dropping nudge")
}
}
})()
w.WriteHeader(http.StatusNoContent)
case "GET":
w.Header().Add("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
file, _ := os.Open("./controller/controller.html")
defer file.Close()
io.Copy(w, file)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
})
mux.HandleFunc("/main.css", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/css")
w.WriteHeader(http.StatusOK)
file, _ := os.Open("./display/main.css")
defer file.Close()
io.Copy(w, file)
})
mux.HandleFunc("/controller.js", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/javascript")
w.WriteHeader(http.StatusOK)
file, _ := os.Open("./controller/controller.js")
defer file.Close()
io.Copy(w, file)
})
log.Println(http.ListenAndServe(":8080", mux))
}()
mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir("./display/")))
mux.HandleFunc("/socket", func(w http.ResponseWriter, r *http.Request) {
con, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
defer con.Close()
var c client
c.nudges = make(chan [2]int)
(func() {
clients.Lock()
defer clients.Unlock()
clients.set[&c] = struct{}{}
})()
defer (func() {
clients.Lock()
defer clients.Unlock()
delete(clients.set, &c)
log.Println("Closing client connection")
})()
con.SetCloseHandler(func(code int, text string) error {
close(c.nudges)
return nil
})
for n := range c.nudges {
err := con.WriteJSON(n)
if err != nil {
log.Println(err)
return
}
}
})
log.Println(http.ListenAndServe(":8081", mux))
}