Skip to content

Instantly share code, notes, and snippets.

@naikrovek
Last active January 27, 2020 16:07
Show Gist options
  • Save naikrovek/9cb2b06509c656c39df03614036a5630 to your computer and use it in GitHub Desktop.
Save naikrovek/9cb2b06509c656c39df03614036a5630 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"database/sql"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"io/ioutil"
"log"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/go-vgo/robotgo"
_ "github.com/mattn/go-sqlite3"
)
var wg sync.WaitGroup
func main() {
quitFlag := false
// channels for communication between goroutines
locations := make(chan image.Point, 10)
db, _ := sql.Open("sqlite3", "./mouse.db")
defer db.Close() // `defer` is very handy.
// storing the date as text here, because sqlite3 doesn't have a datetime datatype.
statement, err := db.Prepare("CREATE TABLE IF NOT EXISTS mouse (time TEXT PRIMARY KEY, x INT, y INT)")
check(err)
_, err = statement.Exec()
check(err)
defer statement.Close()
// set your output image resolution here. it should match your monitor
// geometry: primary monitor's lower left-hand corner is 0,0 so if you have
// monitors below and/or to the left of your primary monitor, windows will
// return negative numbers for the mouse position in those areas.
w, h := robotgo.GetScreenSize()
i := image.NewNRGBA(image.Rect(0, 0, w, h))
// set a white background color
draw.Draw(i, i.Bounds(), &image.Uniform{color.Black}, image.Point{0, 0}, draw.Src)
// 60 times a second, capture the mouse pointer location and shove it in the
// channel set aside for this.
wg.Add(1)
go func() {
for {
if quitFlag {
close(locations)
break
}
// wait until the next interval.
time.Sleep(time.Second / 60)
x, y := robotgo.GetMousePos()
point := image.Point{x, y}
locations <- point
}
wg.Done()
}()
// get the mouse location from the channel and draw it onto the image in
// memory. also - write the date and time and X & Y location to a database.
wg.Add(1)
go func() {
statement, err = db.Prepare("INSERT INTO mouse (time, x, y) VALUES (?, ?, ?)")
check(err)
for {
if quitFlag {
break
}
// wait until a location is ready to be saved to the database.
point := <-locations
// get the current alpha at the point
c := i.NRGBAAt(point.X, point.Y)
newAlpha := c.A
// make the point less transparent if there's already a dot here.
if newAlpha != 0 {
newAlpha/=2
}
i.Set(point.X, point.Y, color.NRGBA{1, 1, 1, newAlpha})
_, err = statement.Exec(time.Now().Format("2006-01-02 15:04:05.000"), point.X, point.Y)
check(err)
}
statement.Close()
wg.Done()
}()
// once a minute, save the current image.
wg.Add(1)
go func() {
for {
if quitFlag {
break
}
time.Sleep(time.Second)
// save at the top of the minute.
if time.Now().Unix()%60 == 0 {
var buf bytes.Buffer
err := png.Encode(&buf, i)
check(err)
err = ioutil.WriteFile("mouse-"+time.Now().Format("2006-01-02")+".png", buf.Bytes(), 0777)
check(err)
}
}
wg.Done()
}()
// handle CTRL+C gracefully
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGTERM, syscall.SIGINT)
go func() {
<-sigc
fmt.Println("stopping.")
quitFlag = true
}()
wg.Wait()
}
// f*ck it, easy mode
func check(e error) {
if e != nil {
log.Panic(e)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment