Files
u-desktop/systray.go
绝尘 9fd3acede3 新增: 星座运势+AI资讯+知识卡片+桌面设置窗口+秒显示开关
- 星座运势: 天聚数行API集成,5维进度条+幸运标签+今日概述
- AI资讯: 天聚数行API,图文布局5条展示,文件缓存2小时刷新
- 知识卡片: AI生成,关键字+提示词配置,30分钟刷新
- 桌面设置: 独立WebView2窗口,760x1350,含壁纸/布局/城市/颜色等配置
- 显示控制: 壁纸/时间/天气/星座/知识/AI资讯独立开关,秒显示开关
- 文件缓存: 星座运势+AI资讯缓存到本地,启动即显示上次数据
- initDone防抖: 防止设置窗口初始化触发卡片重载
2026-05-26 04:34:00 +08:00

176 lines
3.6 KiB
Go

package main
import (
"fmt"
"log"
"os"
"os/exec"
"runtime"
"strconv"
"sync/atomic"
"time"
"unsafe"
"github.com/getlantern/systray"
"github.com/jchv/go-webview2"
)
func onSystrayReady() {
systray.SetIcon(generateIcon())
systray.SetTooltip("动态壁纸引擎")
mSettings := systray.AddMenuItem("桌面设置", "打开设置窗口")
mRestart := systray.AddMenuItem("重启", "重启程序")
mQuit := systray.AddMenuItem("退出", "退出程序")
// 设置窗口
go func() {
for {
<-mSettings.ClickedCh
openSettingsWindow()
}
}()
// 退出
go func() {
<-mQuit.ClickedCh
os.Exit(0)
}()
// 重启
go func() {
for {
<-mRestart.ClickedCh
exe, _ := os.Executable()
exec.Command(exe).Start()
os.Exit(0)
}
}()
go startWebView()
go weatherLoop()
go horoscopeLoop()
go aiNewsLoop()
go bingWallpaperLoop()
go knowledgeLoop()
}
func startWebView() {
runtime.LockOSThread()
workerw := findWorkerW()
if workerw == 0 {
log.Fatal("WorkerW not found")
}
screenW, screenH := getScreenSize()
log.Printf("Screen: %dx%d", screenW, screenH)
wv = webview2.NewWithOptions(webview2.WebViewOptions{
AutoFocus: false,
WindowOptions: webview2.WindowOptions{
Title: "动态壁纸",
Width: uint(screenW),
Height: uint(screenH),
},
})
if wv == nil {
log.Fatal("WebView2 create failed")
}
wv.Bind("setZodiacFromGo", func(zodiac string) error {
cfg := loadConfig()
cfg.Zodiac = zodiac
return saveConfig(cfg)
})
wv.SetHtml(buildWallpaperHTML(loadConfig()))
time.Sleep(1 * time.Second)
wvHwnd = uintptr(wv.Window())
procSetWindowLongPtrW.Call(wvHwnd, uintptr(0xFFFFFFF0), uintptr(0x80000000|0x10000000|0x02000000))
procShowWindow.Call(wvHwnd, 5)
procMoveWindow.Call(wvHwnd, uintptr(^uint(0)), uintptr(^uint(0)), uintptr(screenW+2), uintptr(screenH+2), 1)
log.Printf("壁纸已嵌入: HWND=0x%x, %dx%d", wvHwnd, screenW, screenH)
go func() {
time.Sleep(500 * time.Millisecond)
cfg := loadConfig()
evalJS(fmt.Sprintf(`window.userZodiac = %q;`, cfg.Zodiac))
}()
go fullscreenMonitor()
go func() {
time.Sleep(3 * time.Second)
workerw := findWorkerW()
if workerw != 0 {
oldParent, _, _ := procSetParent.Call(wvHwnd, workerw)
log.Printf("SetParent: 0x%x -> 0x%x (old=0x%x)", wvHwnd, workerw, oldParent)
procMoveWindow.Call(wvHwnd, uintptr(^uint(0)), uintptr(^uint(0)), uintptr(screenW+2), uintptr(screenH+2), 1)
}
}()
type msg struct {
hwnd uintptr
message uint32
wParam uintptr
lParam uintptr
time uint32
pt struct{ x, y int32 }
}
var m msg
for {
ret, _, _ := procGetMessageW.Call(
uintptr(unsafe.Pointer(&m)),
0, 0, 0,
)
if ret == 0 {
break
}
if m.message == wmEvalJS {
for {
select {
case js := <-jsQueue:
wv.Eval(js)
default:
goto nextMsg
}
}
}
if m.message == wmSetHtml {
select {
case html := <-htmlQueue:
wv.SetHtml(html)
default:
}
goto nextMsg
}
nextMsg:
procTranslateMessage.Call(uintptr(unsafe.Pointer(&m)))
procDispatchMessageW.Call(uintptr(unsafe.Pointer(&m)))
}
}
func fullscreenMonitor() {
type rect struct{ Left, Top, Right, Bottom int32 }
var lastState string
for {
if atomic.LoadInt32(&paused) == 0 && wv != nil {
fg, _, _ := procGetForegroundWindow.Call()
if fg != 0 {
var r rect
procGetWindowRect.Call(fg, uintptr(unsafe.Pointer(&r)))
screenW, screenH := getScreenSize()
isFull := (r.Right-r.Left >= screenW) && (r.Bottom-r.Top >= screenH)
state := strconv.FormatBool(isFull)
if state != lastState {
lastState = state
evalJS("if(window.setFullscreen) setFullscreen(" + state + ")")
}
}
}
time.Sleep(2 * time.Second)
}
}