Add system tray icon, file logging, and fix Windows hotkey conflict

- Adds fyne.io/systray with a programmatic monkey face icon and Quit menu item
- Logs to discord-lobby-mute.log next to the exe so startup errors are visible
  without a console window
- Windows now uses raw Win32 RegisterHotKey in a locked goroutine instead of
  golang.design/x/hotkey, avoiding a deadlock where systray and the hotkey
  library both try to own the main thread message loop
- Build with: GOOS=windows GOARCH=amd64 go build -ldflags "-H windowsgui" -o discord-lobby-mute.exe .

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 19:24:20 +03:00
parent 4f94c4f20b
commit 75c113909d
8 changed files with 229 additions and 26 deletions
+68
View File
@@ -0,0 +1,68 @@
package main
import (
"bytes"
"image"
"image/color"
"image/png"
)
func monkeyIcon() []byte {
const size = 32
img := image.NewRGBA(image.Rect(0, 0, size, size))
brown := color.RGBA{110, 72, 38, 255}
tan := color.RGBA{210, 175, 130, 255}
pink := color.RGBA{190, 130, 110, 255}
dark := color.RGBA{15, 8, 3, 255}
white := color.RGBA{255, 255, 255, 255}
// Ears drawn first so the head circle overlaps the inner edge naturally.
fillCircle(img, 4, 13, 5, brown)
fillCircle(img, 28, 13, 5, brown)
fillCircle(img, 4, 13, 3, pink)
fillCircle(img, 28, 13, 3, pink)
fillCircle(img, 16, 15, 13, brown) // head
fillEllipse(img, 16, 21, 7, 4, tan) // muzzle
// Eyes: light surround, dark iris, white highlight.
fillCircle(img, 11, 12, 3, tan)
fillCircle(img, 21, 12, 3, tan)
fillCircle(img, 11, 12, 2, dark)
fillCircle(img, 21, 12, 2, dark)
fillCircle(img, 12, 11, 1, white)
fillCircle(img, 22, 11, 1, white)
// Nostrils.
fillCircle(img, 14, 21, 1, dark)
fillCircle(img, 18, 21, 1, dark)
var buf bytes.Buffer
_ = png.Encode(&buf, img)
return buf.Bytes()
}
func fillCircle(img *image.RGBA, cx, cy, r int, c color.RGBA) {
for y := cy - r; y <= cy+r; y++ {
for x := cx - r; x <= cx+r; x++ {
dx, dy := x-cx, y-cy
if dx*dx+dy*dy <= r*r {
img.SetRGBA(x, y, c)
}
}
}
}
func fillEllipse(img *image.RGBA, cx, cy, rx, ry int, c color.RGBA) {
for y := cy - ry; y <= cy+ry; y++ {
for x := cx - rx; x <= cx+rx; x++ {
dx := float64(x-cx) / float64(rx)
dy := float64(y-cy) / float64(ry)
if dx*dx+dy*dy <= 1.0 {
img.SetRGBA(x, y, c)
}
}
}
}