Files
bidding_watcher/widget/looker/sys_run/systray_run.go

195 lines
6.1 KiB
Go
Raw Normal View History

package main
import (
"bufio"
"fmt"
"io"
"os"
"os/signal"
"syscall"
"github.com/getlantern/systray"
)
var (
sendPipe *os.File
receivePipe *os.File
logFile *os.File
)
func initLog() {
var err error
logFile, err = os.OpenFile("systray_debug.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
// failsafe
return
}
}
func logMsg(format string, args ...interface{}) {
if logFile != nil {
fmt.Fprintf(logFile, format+"\n", args...)
}
}
func main() {
initLog()
logMsg("Systray process started")
// 使用 Stdin 和 Stdout 与主进程通信
// 主进程将 cmd.Stdin 连接到 pipes将 cmd.Stdout 连接到 pipes
// 所以这里我们直接使用 os.Stdin 读取os.Stdout 发送
sendPipe = os.Stdout
receivePipe = os.Stdin
// 启动系统托盘
logMsg("Calling systray.Run")
systray.Run(onReady, onExit)
}
func onReady() {
logMsg("systray onReady called")
// 设置托盘图标 - 使用简单的图标
systray.SetIcon(getIcon())
systray.SetTitle("Bidding Looker")
systray.SetTooltip("Bidding Looker - 招标信息查看器")
// 显示窗口菜单项
mShow := systray.AddMenuItem("显示窗口", "显示主窗口")
mQuit := systray.AddMenuItem("退出", "退出程序")
// 监听来自主进程的消息
go monitorPipe()
// 处理菜单点击
go func() {
for {
select {
case <-mShow.ClickedCh:
logMsg("Show menu clicked")
// 发送显示窗口消息到主进程
sendMessage("SHOW")
case <-mQuit.ClickedCh:
logMsg("Quit menu clicked")
// 发送退出消息到主进程
sendMessage("QUIT")
systray.Quit()
}
}
}()
// 监听系统信号
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigCh
logMsg("Received signal, quitting")
systray.Quit()
}()
}
func onExit() {
logMsg("systray onExit called")
// 清理资源
}
func monitorPipe() {
logMsg("Starting monitorPipe")
reader := bufio.NewReader(receivePipe)
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
logMsg("Pipe EOF, quitting")
// 主进程关闭了管道(可能退出了),我们也退出
systray.Quit()
return
}
logMsg("Pipe read error: %v", err)
fmt.Fprintf(os.Stderr, "Pipe read error: %v\n", err)
return
}
// 处理来自主进程的消息 (日志输出到 Stderr避免污染 Stdout)
logMsg("Received from main: %s", line)
fmt.Fprintf(os.Stderr, "Received from main: %s", line)
}
}
func sendMessage(message string) {
logMsg("Sending message: %s", message)
_, err := sendPipe.WriteString(message + "\n")
if err != nil {
logMsg("Failed to send message: %v", err)
fmt.Fprintf(os.Stderr, "Failed to send message: %v\n", err)
}
}
// 简单的图标数据16x16 像素的简单图标)
// 使用 PNG 格式,这是 systray 库在 Windows 上的推荐格式
func getIcon() []byte {
// PNG 文件签名
// IHDR chunk (图像头): 16x16, 8-bit RGBA
// IDAT chunk (图像数据): 简单的蓝色方块图标
// IEND chunk (文件结束)
return []byte{
// PNG 签名 (8 bytes)
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
// IHDR chunk
0x00, 0x00, 0x00, 0x0D, // Length: 13 bytes
0x49, 0x48, 0x44, 0x52, // Type: IHDR
0x00, 0x00, 0x00, 0x10, // Width: 16
0x00, 0x00, 0x00, 0x10, // Height: 16
0x08, // Bit depth: 8
0x06, // Color type: RGBA
0x00, 0x00, 0x00, // Compression, Filter, Interlace
0x5D, 0x8A, 0x7F, 0xD4, // CRC
// IDAT chunk (使用最简单的 PNG 编码)
0x00, 0x00, 0x01, 0x6D, // Length: 365 bytes
0x49, 0x44, 0x41, 0x54, // Type: IDAT
// 压缩数据 (zlib 格式)
0x78, 0x9C, // zlib header (default compression)
// Deflate 压缩块
0x63, 0x18, 0x19, 0x60, 0x28, 0x55, 0xF6, 0x7F, 0x00, 0x00, 0x00,
0x00, 0x05, 0x00, 0x01, 0x0A, 0x8C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x52, 0x4B, 0x8C, 0x36, // CRC
// IEND chunk
0x00, 0x00, 0x00, 0x00, // Length: 0 bytes
0x49, 0x45, 0x4E, 0x44, // Type: IEND
0xAE, 0x42, 0x60, 0x82, // CRC
}
}