package main import ( "log" "os" "os/exec" "runtime" "strconv" "sync/atomic" "time" "unsafe" "github.com/getlantern/systray" "github.com/jchv/go-webview2" ) func onSystrayReady() { systray.SetIcon(trayIcon) 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() go startPhotoLoop() } func startWebView() { runtime.LockOSThread() log.Println("startWebView: 开始") workerw := findWorkerW() if workerw == 0 { log.Println("ERROR: WorkerW not found") showError("无法找到桌面窗口(WorkerW),程序无法启动。") os.Exit(1) } log.Printf("WorkerW: 0x%x", workerw) screenW, screenH := getScreenSize() log.Printf("Screen: %dx%d", screenW, screenH) log.Println("创建 WebView2...") wv = webview2.NewWithOptions(webview2.WebViewOptions{ AutoFocus: false, WindowOptions: webview2.WindowOptions{ Title: "动态壁纸", Width: uint(screenW), Height: uint(screenH), }, }) if wv == nil { showError("WebView2 创建失败,请确保已安装 WebView2 Runtime。") os.Exit(1) } log.Println("WebView2 创建成功") // 立即获取 HWND 并隐藏窗口,避免闪烁 wvHwnd = uintptr(wv.Window()) procShowWindow.Call(wvHwnd, 0) // SW_HIDE procSetWindowLongPtrW.Call(wvHwnd, gwlStyle, wsPopup|wsVisible|wsChild) // 立即嵌入 WorkerW procSetParent.Call(wvHwnd, workerw) procMoveWindow.Call(wvHwnd, uintptr(^uint(0)), uintptr(^uint(0)), uintptr(screenW+2), uintptr(screenH+2), 1) log.Printf("已嵌入 WorkerW: HWND=0x%x, %dx%d", wvHwnd, screenW, screenH) wv.Bind("setZodiacFromGo", func(zodiac string) error { cfg := loadConfig() cfg.Zodiac = zodiac return saveConfig(cfg) }) log.Println("设置 HTML...") cfg := loadConfig() wv.SetHtml(buildWallpaperHTML(cfg)) time.Sleep(500 * time.Millisecond) // 嵌入完成后再显示 procShowWindow.Call(wvHwnd, 5) log.Println("壁纸窗口已显示") go func() { time.Sleep(500 * time.Millisecond) reloadAllCards() }() go fullscreenMonitor() 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) } }