Files
Meshray-Manager/internal/tray/tray.go
T
2026-06-30 15:14:37 +08:00

227 lines
9.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Windows系统托盘管理
//go:build windows
// +build windows
package tray
import (
_ "embed"
"fmt"
"os/exec"
"runtime"
"github.com/getlantern/systray"
"go.uber.org/zap"
)
//go:embed favicon.ico
var trayIcon []byte
// TrayManager 托盘管理器
type TrayManager struct {
logger *zap.Logger
webPort int
onExit func()
mQuit *systray.MenuItem
mOpenWeb *systray.MenuItem
mStatus *systray.MenuItem
mRestart *systray.MenuItem
}
// NewTrayManager 创建托盘管理器
func NewTrayManager(logger *zap.Logger, webPort int, onExit func()) *TrayManager {
return &TrayManager{
logger: logger,
webPort: webPort,
onExit: onExit,
}
}
// Run 运行托盘(阻塞)
func (t *TrayManager) Run() {
systray.Run(t.onReady, t.onExit)
}
// onReady 托盘初始化
func (t *TrayManager) onReady() {
// 设置托盘图标和提示
systray.SetIcon(trayIcon)
systray.SetTooltip("MeshRay - 智能组网工具")
// 添加菜单项
t.mStatus = systray.AddMenuItem("🟢 状态:运行中", "MeshRay运行状态")
t.mStatus.Disable()
systray.AddSeparator()
t.mOpenWeb = systray.AddMenuItem("🌐 打开管理界面", "打开Web管理界面")
_ = systray.AddMenuItem("️ 关于", "关于MeshRay")
systray.AddSeparator()
_ = systray.AddMenuItem("❓ 帮助", "查看帮助")
_ = systray.AddMenuItem("🔄 检查更新", "检查新版本")
systray.AddSeparator()
t.mRestart = systray.AddMenuItem("🔁 重启服务", "重启MeshRay服务")
t.mQuit = systray.AddMenuItem("❌ 退出", "退出MeshRay")
// 处理菜单点击
go t.handleMenuEvents()
}
// handleMenuEvents 处理菜单事件
func (t *TrayManager) handleMenuEvents() {
for {
select {
case <-t.mOpenWeb.ClickedCh:
t.openWebInterface()
case <-t.mRestart.ClickedCh:
t.restartService()
case <-t.mQuit.ClickedCh:
t.quit()
}
}
}
// openWebInterface 打开Web管理界面
func (t *TrayManager) openWebInterface() {
url := fmt.Sprintf("http://localhost:%d", t.webPort)
t.logger.Info("打开Web管理界面", zap.String("url", url))
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", "start", url)
case "darwin":
cmd = exec.Command("open", url)
default:
cmd = exec.Command("xdg-open", url)
}
if err := cmd.Start(); err != nil {
t.logger.Error("打开浏览器失败", zap.Error(err))
}
}
// restartService 重启服务
func (t *TrayManager) restartService() {
t.logger.Info("重启服务...")
// ✅ P3 阶段 - 简单重启:先退出,然后由外部监控进程重启
// 完整实现需要 Windows 服务控制或守护进程
t.quit()
}
// quit 退出程序
func (t *TrayManager) quit() {
t.logger.Info("退出MeshRay...")
systray.Quit()
if t.onExit != nil {
t.onExit()
}
}
// updateStatus 更新状态
func (t *TrayManager) updateStatus() {
// ✅ P3 阶段 - 暂不实现动态状态更新
// 未来可以检查服务运行状态,更新托盘菜单(如:运行中/已停止)
}
// MeshRayIcon 托盘图标(ICO格式,16x16
// 简单的 "MR" 文字图标
var MeshRayIcon = []byte{
// ICO 头部
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10,
0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x68, 0x04,
0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 图标数据 - 蓝色背景上的白色 "MR" 文字
// 第1行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第2行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第3行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第4行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第5行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第6行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第7行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第8行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第9行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第10行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第11行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第12行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第13行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第14行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第15行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
// 第16行
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
}