package main import ( "path/filepath" "runtime" "sync" "syscall" "unsafe" ) type windowRect struct{ left, top, right, bottom int32 } func windowClass(hwnd int64) string { var value [256]uint16 user32.NewProc("GetClassNameW").Call(uintptr(hwnd), uintptr(unsafe.Pointer(&value[0])), uintptr(len(value))) return syscall.UTF16ToString(value[:]) } func nativeWindowInfo(hwnd int64) obj { if hwnd == 0 { return obj{} } var pid uint32 thread, _, _ := user32.NewProc("GetWindowThreadProcessId").Call(uintptr(hwnd), uintptr(unsafe.Pointer(&pid))) if pid == 0 { return obj{} } visible, _, _ := user32.NewProc("IsWindowVisible").Call(uintptr(hwnd)) owner, _, _ := user32.NewProc("GetWindow").Call(uintptr(hwnd), 4) root, _, _ := user32.NewProc("GetAncestor").Call(uintptr(hwnd), 2) var title [1024]uint16 user32.NewProc("GetWindowTextW").Call(uintptr(hwnd), uintptr(unsafe.Pointer(&title[0])), uintptr(len(title))) app := "" process, _, _ := kernel32.NewProc("OpenProcess").Call(0x1000, 0, uintptr(pid)) if process != 0 { var path [32768]uint16 size := uint32(len(path)) ok, _, _ := kernel32.NewProc("QueryFullProcessImageNameW").Call(process, 0, uintptr(unsafe.Pointer(&path[0])), uintptr(unsafe.Pointer(&size))) if ok != 0 { app = filepath.Base(syscall.UTF16ToString(path[:])) } kernel32.NewProc("CloseHandle").Call(process) } info := obj{"pid": int64(pid), "window_id": hwnd, "thread_id": int64(thread), "root_window_id": int64(root), "owner_id": int64(owner), "class_name": windowClass(hwnd), "title": syscall.UTF16ToString(title[:]), "app_name": app, "is_on_screen": visible != 0, "z_index": nil, "minimized": false} runtime.LockOSThread() defer runtime.UnlockOSThread() previous, _, _ := user32.NewProc("SetThreadDpiAwarenessContext").Call(^uintptr(3)) if previous != 0 { defer user32.NewProc("SetThreadDpiAwarenessContext").Call(previous) var rect windowRect ok, _, _ := user32.NewProc("GetWindowRect").Call(uintptr(hwnd), uintptr(unsafe.Pointer(&rect))) if ok != 0 { info["bounds"] = obj{"x": rect.left, "y": rect.top, "width": rect.right - rect.left, "height": rect.bottom - rect.top} info["is_on_screen"] = visible != 0 && rect.right > rect.left && rect.bottom > rect.top } } return info } // A single static callback avoids NewCallback's non-reclaimable slot leak. var shellEnumerationLock sync.Mutex var shellEnumeration *[]obj var shellEnumerator = syscall.NewCallback(func(hwnd, _ uintptr) uintptr { if shellKind(windowClass(int64(hwnd))) != "" { info := nativeWindowInfo(int64(hwnd)) if info["is_on_screen"] == true { *shellEnumeration = append(*shellEnumeration, info) } } return 1 }) func nativeShellWindows() []obj { shellEnumerationLock.Lock() defer shellEnumerationLock.Unlock() var windows []obj shellEnumeration = &windows user32.NewProc("EnumWindows").Call(shellEnumerator, 0) shellEnumeration = nil return windows } func keyboardFocus() obj { foreground := nativeWindowInfo(foregroundWindow()) var gui struct { size, flags uint32 active, focus, capture, menuOwner, moveSize, caret uintptr rect windowRect } gui.size = uint32(unsafe.Sizeof(gui)) ok, _, _ := user32.NewProc("GetGUIThreadInfo").Call(0, uintptr(unsafe.Pointer(&gui))) result := obj{"probe_ok": ok != 0, "foreground_window": foreground} if ok != 0 { focused := nativeWindowInfo(int64(gui.focus)) result["focus_window"] = focused result["focus_root"] = nativeWindowInfo(num(focused["root_window_id"])) result["active_window"] = nativeWindowInfo(int64(gui.active)) result["menu_owner"] = nativeWindowInfo(int64(gui.menuOwner)) result["menu_active"] = gui.flags&0x1c != 0 } return result }