diff --git a/cmd/panel/icon_other.go b/cmd/panel/icon_other.go new file mode 100644 index 0000000..ba64f8f --- /dev/null +++ b/cmd/panel/icon_other.go @@ -0,0 +1,7 @@ +//go:build !windows + +package main + +import "unsafe" + +func setWindowIcon(windowPtr unsafe.Pointer) {} diff --git a/cmd/panel/icon_windows.go b/cmd/panel/icon_windows.go new file mode 100644 index 0000000..869c643 --- /dev/null +++ b/cmd/panel/icon_windows.go @@ -0,0 +1,85 @@ +//go:build windows + +package main + +import ( + "bytes" + _ "embed" + "encoding/binary" + "os" + "path/filepath" + "syscall" + "unsafe" +) + +//go:embed panel_icon.png +var panelIconPNG []byte + +func setWindowIcon(windowPtr unsafe.Pointer) { + hwnd := uintptr(windowPtr) + if hwnd == 0 { + return + } + + // Write a temp ICO file (Windows LoadImage needs a file path) + icoData := wrapPNG(panelIconPNG, 64, 64) + icoPath := filepath.Join(os.TempDir(), "syncwarden-panel.ico") + if err := os.WriteFile(icoPath, icoData, 0644); err != nil { + return + } + + user32 := syscall.NewLazyDLL("user32.dll") + loadImage := user32.NewProc("LoadImageW") + sendMessage := user32.NewProc("SendMessageW") + + pathW, _ := syscall.UTF16PtrFromString(icoPath) + + const ( + imageIcon = 1 + lrLoadFromFile = 0x0010 + wmSetIcon = 0x0080 + iconBig = 1 + iconSmall = 0 + ) + + hBig, _, _ := loadImage.Call(0, uintptr(unsafe.Pointer(pathW)), imageIcon, 32, 32, lrLoadFromFile) + if hBig != 0 { + sendMessage.Call(hwnd, wmSetIcon, iconBig, hBig) + } + + hSmall, _, _ := loadImage.Call(0, uintptr(unsafe.Pointer(pathW)), imageIcon, 16, 16, lrLoadFromFile) + if hSmall != 0 { + sendMessage.Call(hwnd, wmSetIcon, iconSmall, hSmall) + } +} + +func wrapPNG(pngData []byte, width, height int) []byte { + const headerSize = 6 + const entrySize = 16 + imageOffset := headerSize + entrySize + + buf := new(bytes.Buffer) + binary.Write(buf, binary.LittleEndian, uint16(0)) + binary.Write(buf, binary.LittleEndian, uint16(1)) + binary.Write(buf, binary.LittleEndian, uint16(1)) + + w := byte(width) + if width >= 256 { + w = 0 + } + h := byte(height) + if height >= 256 { + h = 0 + } + buf.WriteByte(w) + buf.WriteByte(h) + buf.WriteByte(0) + buf.WriteByte(0) + binary.Write(buf, binary.LittleEndian, uint16(1)) + binary.Write(buf, binary.LittleEndian, uint16(32)) + binary.Write(buf, binary.LittleEndian, uint32(len(pngData))) + binary.Write(buf, binary.LittleEndian, uint32(imageOffset)) + + buf.Write(pngData) + return buf.Bytes() +} diff --git a/cmd/panel/main.go b/cmd/panel/main.go index ef774cf..66d5eb1 100644 --- a/cmd/panel/main.go +++ b/cmd/panel/main.go @@ -19,6 +19,10 @@ func main() { w.SetTitle("SyncWarden — Admin Panel") w.SetSize(1024, 768, webview.HintNone) + + // Set window icon (title bar + taskbar) + setWindowIcon(w.Window()) + w.Navigate(*addr) w.Run() } diff --git a/cmd/panel/panel_icon.png b/cmd/panel/panel_icon.png new file mode 100644 index 0000000..85e61b1 Binary files /dev/null and b/cmd/panel/panel_icon.png differ