Skip to content

Instantly share code, notes, and snippets.

@wybiral
Last active September 11, 2023 08:53
Show Gist options
  • Save wybiral/c8f46fdf1fc558d631b55de3a0267771 to your computer and use it in GitHub Desktop.
Save wybiral/c8f46fdf1fc558d631b55de3a0267771 to your computer and use it in GitHub Desktop.
Tracking cursor position in real-time with remote monitoring (without JavaScript)
// Tracking cursor position in real-time without JavaScript
// Demo: https://twitter.com/davywtf/status/1124146339259002881
package main
import (
"fmt"
"net/http"
"strings"
)
const W = 50
const H = 50
var ch chan string
const head = `<head>
<style>
*{margin:0;padding:0}
html,body{width:100%;height:100%}
p{
width:10px;
height:10px;
display:inline-block;
border-right:1px solid #666;
border-bottom:1px solid #666
}
</style>
</head>`
func main() {
ch = make(chan string)
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
if p == "/" {
index(w, r)
return
} else if p == "/watch" {
watch(w, r)
return
} else {
if strings.HasPrefix(p, "/c") && strings.HasSuffix(p, ".png") {
ch <- p[1 : len(p)-4]
}
}
}
func index(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
flusher, ok := w.(http.Flusher)
if !ok {
return
}
w.Write([]byte(head))
flusher.Flush()
// Send <p> grid
w.Write([]byte("<body>\n"))
for i := 0; i < H; i++ {
w.Write([]byte("<div>"))
for j := 0; j < W; j++ {
w.Write([]byte(fmt.Sprintf("<p id=\"c%dx%d\"></p>", i, j)))
}
w.Write([]byte("</div>\n"))
}
w.Write([]byte("</body>\n"))
flusher.Flush()
// Send CSS
w.Write([]byte("<style>"))
for i := 0; i < H; i++ {
for j := 0; j < W; j++ {
id := fmt.Sprintf("c%dx%d", i, j)
s := fmt.Sprintf("#%s:hover{background:url(\"%s.png\")}", id, id)
w.Write([]byte(s))
}
}
w.Write([]byte("</style>"))
flusher.Flush()
}
func watch(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
flusher, ok := w.(http.Flusher)
if !ok {
return
}
w.Write([]byte(head))
flusher.Flush()
// Send <p> grid
w.Write([]byte("<body>\n"))
for i := 0; i < H; i++ {
w.Write([]byte("<div>"))
for j := 0; j < W; j++ {
w.Write([]byte(fmt.Sprintf("<p id=\"c%dx%d\"></p>", i, j)))
}
w.Write([]byte("</div>\n"))
}
w.Write([]byte("</body>\n"))
flusher.Flush()
// Listen to ch for updates and write to response
for p := range ch {
s := fmt.Sprintf("<style>#%s{background:#000}</style>\n", p)
_, err := w.Write([]byte(s))
if err != nil {
return
}
flusher.Flush()
}
}
@fr3fou
Copy link

fr3fou commented Jul 2, 2019

Yea, I figured it out by rewatching the demo and noticing the page never finished loading ^^

Thanks for the quick reply, though! :>

@AHP873
Copy link

AHP873 commented Jul 25, 2019

Where does the data go or how would i be able to capture it?

@allyraza
Copy link

it pretty simple actually there two windows open as we can see in the demo, one move the cursor other one draws

  1. connections are kept open and response is sent in chunks
  2. one handler send blocks with style hover background image
  3. on hover image is requested from the server ( secret sauce making it tick)
  4. handler for image with coordinates (x,y) puts the data on the go channel
  5. watch handler go throughs the channel and push it to the other window

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment