Files
auto_dark_light_windows/main.go

195 lines
4.5 KiB
Go
Raw Normal View History

2025-04-19 20:05:02 +07:00
package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"time"
"unsafe"
"github.com/getlantern/systray"
"golang.org/x/sys/windows/registry"
)
2025-11-26 08:09:26 +07:00
// Define required structures for OS version check
const (
VER_MAJORVERSION = 0x001
VER_MINORVERSION = 0x002
VER_BUILDNUMBER = 0x004
)
type RTL_OSVERSIONINFOW struct {
OSVersionInfoSize uint32
MajorVersion uint32
MinorVersion uint32
BuildNumber uint32
PlatformId uint32
CSDVersion [128]uint16
}
// isWindows11OrNewer checks if the OS is Windows 11 (Build >= 22000) or later.
func isWindows11OrNewer() bool {
ntdll := syscall.NewLazyDLL("ntdll.dll")
rtlGetVersion := ntdll.NewProc("RtlGetVersion")
var info RTL_OSVERSIONINFOW
info.OSVersionInfoSize = uint32(unsafe.Sizeof(info))
ret, _, _ := rtlGetVersion.Call(uintptr(unsafe.Pointer(&info)))
// Check if the call was successful (NTSTATUS 0)
if ret != 0 {
return false // Assume older or error
}
// Windows 11 is generally defined by Build Number >= 22000
return info.MajorVersion >= 10 && info.BuildNumber >= 22000
}
2025-04-19 20:05:02 +07:00
// Set Windows dark/light mode via registry
func setMode(light bool) {
key, _ := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Themes\Personalize`, registry.SET_VALUE)
defer key.Close()
val := uint32(0)
if light {
val = 1
}
key.SetDWordValue("AppsUseLightTheme", val)
key.SetDWordValue("SystemUsesLightTheme", val)
}
// Restart Explorer to fully apply theme
func restartExplorer() {
exec.Command("taskkill", "/f", "/im", "explorer.exe").Run()
time.Sleep(5 * time.Second)
2025-04-19 20:05:02 +07:00
exec.Command("explorer.exe").Start()
}
// Get current theme mode from registry
func getCurrentMode() string {
key, _ := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Themes\Personalize`, registry.QUERY_VALUE)
defer key.Close()
val, _, _ := key.GetIntegerValue("AppsUseLightTheme")
if val == 1 {
return "light"
}
return "dark"
}
// Find image in same dir with any supported extension
func findImageFile(name string) string {
exePath, _ := os.Executable()
dir := filepath.Dir(exePath)
matches, _ := filepath.Glob(filepath.Join(dir, name+".*"))
for _, f := range matches {
ext := strings.ToLower(filepath.Ext(f))
if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" || ext == ".webp" {
return f
}
}
return ""
}
// Get current wallpaper path from registry
func getCurrentWallpaper() string {
key, _ := registry.OpenKey(registry.CURRENT_USER, `Control Panel\Desktop`, registry.QUERY_VALUE)
defer key.Close()
val, _, _ := key.GetStringValue("Wallpaper")
return val
}
// Set desktop wallpaper via Windows API
func setWallpaper(path string) {
user32 := syscall.NewLazyDLL("user32.dll")
sysParam := user32.NewProc("SystemParametersInfoW")
p, _ := syscall.UTF16PtrFromString(path)
sysParam.Call(0x0014, 0, uintptr(unsafe.Pointer(p)), uintptr(0x01|0x02))
}
// Main background loop
func backgroundLoop() {
lightImg := findImageFile("light")
darkImg := findImageFile("dark")
lastMode := getCurrentMode()
2025-11-26 08:09:26 +07:00
// Determine if explorer restart is needed once at startup
needsExplorerRestart := isWindows11OrNewer()
if !needsExplorerRestart {
fmt.Println("Running on Windows 10 (or older), skipping explorer restart.")
} else {
fmt.Println("Running on Windows 11 (or newer), explorer restart enabled.")
}
2025-04-19 20:05:02 +07:00
for {
hour := time.Now().Hour()
newMode := "dark"
var targetImg string
if hour >= 6 && hour < 18 {
newMode = "light"
targetImg = lightImg
} else {
targetImg = darkImg
}
if newMode != lastMode {
setMode(newMode == "light")
2025-11-26 08:09:26 +07:00
// Conditional Explorer Restart ( Windows 10 or 11)
if needsExplorerRestart {
restartExplorer()
}
2025-04-19 20:05:02 +07:00
lastMode = newMode
}
if targetImg != "" && getCurrentWallpaper() != targetImg {
setWallpaper(targetImg)
}
time.Sleep(60 * time.Second)
}
}
// Load icon from local directory (icon.ico)
func getIconBytes() []byte {
exePath, _ := os.Executable()
iconPath := filepath.Join(filepath.Dir(exePath), "icon.ico")
data, err := ioutil.ReadFile(iconPath)
if err != nil {
fmt.Println("icon.ico not found or unreadable.")
return nil
}
return data
}
// Systray startup
func onReady() {
icon := getIconBytes()
if icon != nil {
systray.SetIcon(icon)
}
systray.SetTitle("Auto Theme")
systray.SetTooltip("Auto Theme + Wallpaper Switcher")
mQuit := systray.AddMenuItem("Quit", "Exit the program")
go func() {
go backgroundLoop()
<-mQuit.ClickedCh
systray.Quit()
}()
}
func onExit() {}
func main() {
systray.Run(onReady, onExit)
}