diff --git a/.gitignore b/.gitignore index e660fd9..231bd82 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ bin/ +TwentyTwentyTwenty.app/ diff --git a/Makefile b/Makefile index b5ebeb4..41281a2 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,14 @@ -all: bin/twenty-twenty-twenty-windows-386 bin/twenty-twenty-twenty-windows-amd64 \ - bin/twenty-twenty-twenty-darwin-arm64 bin/twenty-twenty-twenty-darwin-amd64 \ - bin/twenty-twenty-twenty-linux-arm64 bin/twenty-twenty-twenty-linux-amd64 +.PHONY: clean +export CGO_ENABLED := 1 -bin/twenty-twenty-twenty-%: bin/twenty-twenty-twenty - GOOS=$(word 1,$(subst -, ,$*)) GOARCH=$(word 2,$(subst -, ,$*)) go build -o $@ +all: bin/twenty-twenty-twenty -bin/twenty-twenty-twenty: eye-solid.svg *.go go.mod go.sum - go test +TwentyTwentyTwenty.app: bin/twenty-twenty-twenty + gogio -target macos -icon ./eye.png -o $@ . + codesign -s - $@/*.app + +bin/twenty-twenty-twenty: eye.png *.go go.mod go.sum go build -o $@ + +clean: + rm -rf bin TwentyTwentyTwenty.app diff --git a/README.md b/README.md index beddf28..f6dc0a3 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@ screen usage sessions. ``` $ ./twenty-twenty-twenty -help Usage of ./twenty-twenty-twenty: - -duration int - how long to show the notification in seconds (does not work in macOS) (default 20) - -frequency int - how often to show the notification in minutes (default 20) + -duration uint + how long each pause should be in seconds (default 20) + -frequency uint + how often the pause should be in minutes (default 20) $ ./twenty-twenty-twenty # the defaults are recommended ``` @@ -24,18 +24,16 @@ Needs Go 1.18+. ``` $ go build +# or +$ make ``` -## License +Also in macOS, it needs to be built with `gogio`: -The code itself is licensed in the MIT license. - -[beeep](https://github.com/gen2brain/beeep) is the only third-party -dependency, and it is licensed in [BSD -2-clause](https://github.com/gen2brain/beeep/blob/master/LICENSE). - -The eye icon is from Font Awesome and is licensed in [CC-BY -4.0](https://creativecommons.org/licenses/by/4.0/). +``` +$ go install gioui.org/cmd/gogio@0.4.0 +$ make TwentyTwentyTwenty.app +``` [1]: https://www.allaboutvision.com/conditions/refractive-errors/what-is-20-20-20-rule/ [2]: https://pubmed.ncbi.nlm.nih.gov/36473088/ diff --git a/eye-solid.svg b/eye-solid.svg deleted file mode 100644 index 186dd4d..0000000 --- a/eye-solid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/eye.png b/eye.png new file mode 100644 index 0000000..dccc47c Binary files /dev/null and b/eye.png differ diff --git a/go.mod b/go.mod index e84cce0..de23dda 100644 --- a/go.mod +++ b/go.mod @@ -2,12 +2,23 @@ module github.com/thiagokokada/twenty-twenty-twenty go 1.18 -require github.com/gen2brain/beeep v0.0.0-20230907135156-1a38885a97fc +require ( + gioui.org v0.4.0 + gioui.org/x v0.4.0 +) require ( - github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect + gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect + gioui.org/shader v1.0.8 // indirect + git.sr.ht/~jackmordaunt/go-toast v1.0.0 // indirect + git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 // indirect + github.com/esiqveland/notify v0.11.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect - github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/image v0.7.0 // indirect golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.9.0 // indirect ) diff --git a/go.sum b/go.sum index ce7df5f..8e83746 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,65 @@ -github.com/gen2brain/beeep v0.0.0-20230907135156-1a38885a97fc h1:NNgdMgPX3j33uEAoVVxNxillDPnxT0xbGv8uh4CKIAo= -github.com/gen2brain/beeep v0.0.0-20230907135156-1a38885a97fc/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4= -github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= -github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10= +eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY= +gioui.org v0.4.0 h1:Dsp2Z693xSokLt4axSCDdAiT6vvEY2I9gadIeZgFqxg= +gioui.org v0.4.0/go.mod h1:2atiYR4upH71/6ehnh6XsUELa7JZOrOHHNMDxGBZF0Q= +gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= +gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc= +gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= +gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA= +gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= +gioui.org/x v0.4.0 h1:H6DofC86KoG51wgzeeA4ujZDDfcIa8vbL+jD9SpF/D8= +gioui.org/x v0.4.0/go.mod h1:YAoFl2lbeARk4LopDXHK1N7fBQJupPYDSm9maf6tFlM= +git.sr.ht/~jackmordaunt/go-toast v1.0.0 h1:bbRox6VkotdOj3QcWimZQ84APoszIsA/pSIj8ypDdV8= +git.sr.ht/~jackmordaunt/go-toast v1.0.0/go.mod h1:aIuRX/HdBOz7yRS8rOVYQCwJQlFS7DbYBTpUV0SHeeg= +git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 h1:bGG/g4ypjrCJoSvFrP5hafr9PPB5aw8SjcOWWila7ZI= +git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo= +github.com/esiqveland/notify v0.11.0 h1:0WJ/xW+3Ln8uRBYntG7f0XihXxnlOaQTdha1yyzXz30= +github.com/esiqveland/notify v0.11.0/go.mod h1:63UbVSaeJwF0LVJARHFuPgUAoM7o1BEvCZyknsuonBc= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo= +github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k= +github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= -github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= -github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= -github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFdfI8wCeyqgdXWN4+CkFVNPAT0= +golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8= +golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw= +golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index 6b8c1da..7e7787a 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,13 @@ package main import ( - _ "embed" "flag" "fmt" "log" - "os" "time" - "github.com/gen2brain/beeep" + "gioui.org/app" + "gioui.org/x/notify" ) type flags struct { @@ -16,65 +15,58 @@ type flags struct { frequencyInMin uint64 } -//go:embed eye-solid.svg -var iconBytes []byte - -func loadIcon() *os.File { - icon, err := os.CreateTemp("", "twenty-twenty-twenty-icon-") - if err != nil { - log.Fatalf("Error while creating temporary file: %v", err) - } - icon.Write(iconBytes) - return icon -} - func parseFlags() flags { durationInSec := flag.Uint( "duration", 20, - "how long to show the notification in seconds (does not work in macOS)", + "how long each pause should be in seconds", ) frequencyInMin := flag.Uint64( "frequency", 20, - "how often to show the notification in minutes", + "how often the pause should be in minutes", ) flag.Parse() return flags{durationInSec: *durationInSec, frequencyInMin: *frequencyInMin} } -func initBeeep(durationInSec uint) { - const MS_IN_SEC = 1000 +func main() { + flags := parseFlags() - err := beeep.Beep(beeep.DefaultFreq, int(durationInSec)*MS_IN_SEC) + notifier, err := notify.NewNotifier() if err != nil { - log.Fatalf("Error during beeep init: %v\n", err) + log.Fatalf("Error while creating a notifier: %v\n", err) } -} - -func main() { - icon := loadIcon() - defer icon.Close() - defer os.Remove(icon.Name()) - flags := parseFlags() - initBeeep(flags.durationInSec) + _, err = notifier.CreateNotification( + "Starting 20-20-20", + fmt.Sprintf("You will see a notification every %d minutes(s)", flags.frequencyInMin), + ) + if err != nil { + log.Fatalf("Error while sending test notification: %v\n", err) + } ticker := time.NewTicker(time.Duration(flags.frequencyInMin) * time.Minute) - fmt.Printf("Running twenty-twenty-twenty every %d minute(s)...\n", flags.frequencyInMin) - - for { - <-ticker.C - log.Println("Sending notification...") - err := beeep.Alert( - "Time to rest your eyes", - fmt.Sprintf("Look at 20 feet (~6 meters) away for %d seconds", flags.durationInSec), - icon.Name(), - ) - if err != nil { - log.Fatalf("Error during beeep alert: %v\n", err) + go func() { + for { + <-ticker.C + log.Println("Sending notification...") + go func() { + notification, err := notifier.CreateNotification( + "Time to rest your eyes", + fmt.Sprintf("Look at 20 feet (~6 meters) away for %d seconds", flags.durationInSec), + ) + if err != nil { + log.Printf("Error while sending notification: %v\n", err) + return + } + time.Sleep(time.Duration(flags.durationInSec) * time.Second) + notification.Cancel() + }() } - } + }() + + app.Main() } diff --git a/main_test.go b/main_test.go deleted file mode 100644 index 2941f26..0000000 --- a/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "math" - "os" - "testing" -) - -func TestLoadIcon(t *testing.T) { - icon := loadIcon() - f, err := os.Open(icon.Name()) - if err != nil { - t.Error(err) - } - defer f.Close() - - stat, err := f.Stat() - if err != nil { - t.Error(err) - } - if stat.Size() <= 100 { - t.Errorf("Icon should be at least 100 bytes, it was %d", stat.Size()) - } -} - -func TestInitBeeep(t *testing.T) { - initBeeep(1) - initBeeep(10) - initBeeep(100) - initBeeep(1000) - initBeeep(math.MaxUint64) -}