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) } }