Taking control of another window with Go - go

I'm wondering if there are any libraries that help me take control of another window. For example if user has calc.exe running, I'd like my go code to move it, resize it, maybe even remove it's frame, attach stuff to it, idk.
Right now I only know how to do it with scripting languages like autoit or autohotkey or whatnot.

Yes there are several libraries which can be found using godoc.org or go-search.org. In this example I'm using w32 and w32syscall (which supplies some additional functions):
package main
import (
"log"
"strings"
"syscall"
"github.com/AllenDang/w32"
"github.com/hnakamur/w32syscall"
)
func main() {
err := w32syscall.EnumWindows(func(hwnd syscall.Handle, lparam uintptr) bool {
h := w32.HWND(hwnd)
text := w32.GetWindowText(h)
if strings.Contains(text, "Calculator") {
w32.MoveWindow(h, 0, 0, 200, 600, true)
}
return true
}, 0)
if err != nil {
log.Fatalln(err)
}
}
Both of these libraries are merely exposing the underlying win32 API with minimal wrapping, so you will have to read the corresponding documentation from Microsoft to really know how to use them.

Related

Go TUI programming using TCell API

I am trying to learn TUI programming in Go using the TCell API.
It is a simple app that print word "hello". However, when I run the program below, nothing happens. Please tell me what I am doing wrong.
package main
import (
"fmt"
"github.com/gdamore/tcell"
"os"
)
func main() {
scn, err := tcell.NewScreen()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
hhh := []rune("hello")
scn.SetContent(10, 10, rune(' '), hhh, tcell.StyleDefault)
scn.Show()
}
The creator of this api (https://github.com/gdamore/tcell.git) provided the solution. Here is his respond:
There are three potential issues.
First, you need to initialize the screen. Call scn.Init() after creating the screen.
The second is that your call to SetContent is misguided. The string you are passing is to accommodate combining characters. Instead you need to call SetContent 5 times (one for each letter of "hello") with a different offset, and the appropriate letter of "hello". You probably want to just pass "" for the 4th argument (the string), since none of this is combining characters..
The third problem is that your program just exits. On most terminals this will cause the reset of the terminal to occur, losing your output. (On xterm, for example, tcell uses the alternate screen buffer by default, which leads to exit causing the contents of that screen to be lost, when it switches back to the primary screen buffer at program termination.) The simplest way to prove this is to add a time.Sleep(time.Second * 10) or similar as the last line of your program.
Here is the modified code:
import (
"fmt"
"github.com/gdamore/tcell"
"github.com/gdamore/tcell/encoding"
"os"
"time"
)
func main() {
encoding.Register()
scn, err := tcell.NewScreen()
scn.Init()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
scn.Clear()
scn.SetContent(10, 10, rune('h'), []rune(""), tcell.StyleDefault)
scn.Show()
time.Sleep(time.Second * 2)
}
Hope this help.

Getting window geometry in Go for Windows

I want to create a tool with Go that lets me resize multiple windows on my screen. As an example lets assume that I want to find my Firefox window and my Atom (text editor) window and place them, so that they take up exactly half of my screen (FF left, Atom right).
So far I realized, that I need to use the Windows API for that. I created a method that gives me all handles and the titles of all windows, but I'm struggling with geometry information. I understand that the api call GetWindowRect will help, but how can I get the information out of a pointer to a rect?
Follow up question 1: what other information can I get about the windows?
Follow up question 2: How do I resize the window so that it takes exactly half my screen size? I guess, I need another call to get the monitor dimensions.
What I have so far is the code below. The main program finds all handles and displays those containing 'Atom' in the title. The windows package contains the code accessing the windows API.
My current result is that I get 2 handles for atom (why not just 1?). I guess, I have to learn more about the Windows API, too. Are there good summaries to understand the basics?
main.go:
package main
import (
"resizer/windows"
"fmt"
"log"
"strings"
)
func main() {
const title = "Atom"
m := windows.GetAllWindows()
fmt.Printf("Map of windows: \n")
for handle := range m {
if strings.Contains(m[handle].Title(), title) {
fmt.Printf("'%v'\n", m[handle])
}
}
}
windows.go:
package windows
import (
"fmt"
"log"
"syscall"
"unsafe"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
procEnumWindows = user32.MustFindProc("EnumWindows")
procGetWindowTextW = user32.MustFindProc("GetWindowTextW")
)
// Window represents any Window that is opened in the Windows OS
type Window struct {
handle syscall.Handle
title string
}
// Title returns the title of the window
func (w Window) Title() string {
return w.title
}
// GetAllWindows finds all currently opened windows
func GetAllWindows() map[syscall.Handle]Window {
m := make(map[syscall.Handle]Window)
cb := syscall.NewCallback(func(h syscall.Handle, p uintptr) uintptr {
bytes := make([]uint16, 200)
_, err := GetWindowText(h, &bytes[0], int32(len(bytes)))
title := "||| no title found |||"
if err == nil {
title = syscall.UTF16ToString(bytes)
}
m[h] = Window{h, title}
return 1 // continue enumeration
})
EnumWindows(cb, 0)
return m
}
// EnumWindows loops through all windows and calls a callback function on each
func EnumWindows(enumFunc uintptr, lparam uintptr) (err error) {
r1, _, e1 := syscall.Syscall(procEnumWindows.Addr(), 2, uintptr(enumFunc), uintptr(lparam), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
// GetWindowText gets the title of a Window given by a certain handle
func GetWindowText(hwnd syscall.Handle, str *uint16, maxCount int32) (len int32, err error) {
r0, _, e1 := syscall.Syscall(procGetWindowTextW.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(str)), uintptr(maxCount))
len = int32(r0)
if len == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
GetWindowRect() writes the geometry to the RECT structure you pass the pointer to in. It operates exactly like the GetWindowText() call you already have; the difference is you have to provide the RECT structure yourself.
You should be able to just get away with copying the structure verbatim. To substitute data types, use this page. The definition for RECT says all the fields are LONG, which that page says is "[a] 32-bit signed integer". So this should suffice:
type RECT struct {
left int32 // or Left, Top, etc. if this type is to be exported
top int32
right int32
bottom int32
}
(Most likely irrelevant, but it's worth pointing out that RECT operates identically to image.Rectangle, with left and top being Min and right and bottom being Max. They are not identical because image.Rectangle uses int, so you may want to consider providing conversion functions if you want to use image's geometry functions to manipulate rectangles instead of GDI's.)

Making a full screen Terminal application with Go

I'm trying to build a full screen terminal application. I'm using Go as my language of choice. I've figured out how to read from os.Stdin, but I'm unclear on how to clear the terminal window and manipulate the cursor position. I also want to capture the terminal input without it being printed (echoed back).
My questions are:
How can I effectively clear and print to the terminal with column/row coordinates?
How do I stop the terminal from printing keys pressed
My intent:
I want to create a full screen terminal application that renders it's own UI and handles input internally (hot keys/navigation/etc...).
If there are any libraries that cover this sort of use case please feel free to suggest them.
The easiest way to clear the terminal and set position is via ansi escape codes. However, this may not be the ideal way as variation in terminals may come back to bite you.
fmt.Print("\033[2J") //Clear screen
fmt.Printf("\033[%d;%dH", line, col) // Set cursor position
A better alternative would be to use a library like goncurses or termbox-go (credit: second is from Tim Cooper's comment).
With such a library you can do things like this:
import (
gc "code.google.com/p/goncurses"
)
func main() {
s, err := gc.Init()
if err != nil {
panic(err)
}
defer gc.End()
s.Move(5, 2)
s.Println("Hello")
s.GetChar()
}
Code above copied from Rosetta Code
As of December 2019, I would recommend using rivo/tview library.
(goncurses mentioned by #vastlysuperiorman has not been updated since June 2019 and termbox-go is explicitly declared unmaintained).
Here's the "hello world" app, taken from the project's README (reformatted for readability):
package main
import (
"github.com/rivo/tview"
)
func main() {
box := tview.NewBox().
SetBorder(true).
SetTitle("Hello, world!")
if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil {
panic(err)
}
}
tview provides screenshots and example code as well as the standard godoc reference.
To stop the terminal from printing keys pressed you can use the below code:
import (
"fmt"
"syscall"
"golang.org/x/crypto/ssh/terminal"
)
func main(){
fmt.Print("Enter Value: ")
byteInput, _ := terminal.ReadPassword(int(syscall.Stdin))
input:= string(byteInput)
fmt.Println() // it's necessary to add a new line after user's input
fmt.Printf("Your input is '%s'", input)
}

Using Golang to get Windows idle time (GetLastInputInfo or similar)

Is there an example or method of getting a Windows system's idle time using Go?
I've been looking at the documentation at the Golang site but I think I'm missing how to access (and use) the API to get system information including the idle time.
Go's website is hardcoded to show the documentation for the standard library packages on Linux. You will need to get godoc and run it yourself:
go get golang.org/x/tools/cmd/godoc
godoc --http=:6060
then open http://127.0.0.1:6060/ in your web browser.
Of note is package syscall, which provides facilities for accessing functions in DLLs, including UTF-16 helpers and callback generation functions.
Doing a quick recursive search of the Go tree says it doesn't have an API for GetLastInputInfo() in particular, so unless I'm missing something, you should be able to call that function from the DLL directly:
user32 := syscall.MustLoadDLL("user32.dll") // or NewLazyDLL() to defer loading
getLastInputInfo := user32.MustFindProc("GetLastInputInfo") // or NewProc() if you used NewLazyDLL()
// or you can handle the errors in the above if you want to provide some alternative
r1, _, err := getLastInputInfo.Call(uintptr(arg))
// err will always be non-nil; you need to check r1 (the return value)
if r1 == 0 { // in this case
panic("error getting last input info: " + err.Error())
}
Your case involves a structure. As far as I know, you can just recreate the structure flat (keeping fields in the same order), but you must convert any int fields in the original to int32, otherwise things will break on 64-bit Windows. Consult the Windows Data Types page on MSDN for the appropriate type equivalents. In your case, this would be
var lastInputInfo struct {
cbSize uint32
dwTime uint32
}
Because this (like so many structs in the Windows API) has a cbSize field that requires you to initialize it with the size of the struct, we must do so too:
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
Now we just need to pass a pointer to that lastInputInfo variable to the function:
r1, _, err := getLastInputInfo.Call(
uintptr(unsafe.Pointer(&lastInputInfo)))
and just remember to import syscall and unsafe.
All args to DLL/LazyDLL.Call() are uintptr, as is the r1 return. The _ return is never used on Windows (it has to do with the ABI used).
Since I went over most of what you need to know to use the Windows API in Go that you can't gather from reading the syscall docs, I will also say (and this is irrelevant to the above question) that if a function has both ANSI and Unicode versions, you should use the Unicode versions (W suffix) and the UTF-16 conversion functions in package syscall for best results.
I think that's all the info you (or anyone, for that matter) will need to use the Windows API in Go programs.
Regarding for answer from andlabs. This is ready for use example:
import (
"time"
"unsafe"
"syscall"
"fmt"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
kernel32 = syscall.MustLoadDLL("kernel32.dll")
getLastInputInfo = user32.MustFindProc("GetLastInputInfo")
getTickCount = kernel32.MustFindProc("GetTickCount")
lastInputInfo struct {
cbSize uint32
dwTime uint32
}
)
func IdleTime() time.Duration {
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
currentTickCount, _, _ := getTickCount.Call()
r1, _, err := getLastInputInfo.Call(uintptr(unsafe.Pointer(&lastInputInfo)))
if r1 == 0 {
panic("error getting last input info: " + err.Error())
}
return time.Duration((uint32(currentTickCount) - lastInputInfo.dwTime)) * time.Millisecond
}
func main() {
t := time.NewTicker(1 * time.Second)
for range t.C {
fmt.Println(IdleTime())
}
}
This is code print idle time every second. Try run and don't touch mouse/keyboard

Capture the screen in Go?

Is there is a cross-platform way to capture the screen in Google's Go? Or any way for that matter, but cross platform would be preferred.
Now there is:
https://github.com/vova616/screenshot
go get github.com/vova616/screenshot
Example:
package main
import "github.com/vova616/screenshot"
func main() {
img, err := screenshot.CaptureScreen() // *image.RGBA
myImg := image.Image(img) // can cast to image.Image, but not necessary
}
If you need macOS support as well (until the pull request is merged), get:
https://github.com/kesarion/screenshot
Unfortunately, there is no library to do this. There are a couple of bindings for magickwand (C programming language and the ImageMagick image processing libraries), see http://go-lang.cat-v.org/library-bindings but these are incomplete and do not have the screen capture feature.
Meanwhile as GeertJohan suggested, you can use os.exec to run an external program and capture the screen (see sample code below). For example, you can use import command from imagemagick to capture screen (should work on a platform that can run imagemagick)
package main
import (
"bytes"
"fmt"
"log"
"os/exec"
)
func main() {
var buf bytes.Buffer
path, err := exec.LookPath("import")
if err != nil {
log.Fatal("import not installed !")
}
fmt.Printf("import is available at %s\n", path)
cmd := exec.Command("import", "-window", "root", "root.png")
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
if err != nil {
panic(err)
}
fmt.Println(buf.String())
}
I don't know of any cross-platform library, but you can do this with the xgbutil library when an X server is present. You can see an example of how to capture a screenshot here.
If you wanted to get this working on Mac/Windows systems, I'd probably start by examining the source for go.wde, which includes backends for Windows and Mac. I doubt you'll directly find code to capture a screenshot in there, but it might give you some hints or a path to follow.
There is no cross-platform way to capture the screen in Google's Go, because capturing screen relies on a specific API of underlying operating systems. But there are libraries for Go that do this.
For example https://github.com/vova616/screenshot
This library seems to meet your needs:
https://godoc.org/github.com/kbinani/screenshot
captures screen-shot image as image.RGBA. Mac, Windows, Linux, FreeBSD, OpenBSD, NetBSD, and Solaris are supported.
func Capture(x, y, width, height int) (*image.RGBA, error)
func CaptureDisplay(displayIndex int) (*image.RGBA, error)
func CaptureRect(rect image.Rectangle) (*image.RGBA, error)
func GetDisplayBounds(displayIndex int) image.Rectangle
func NumActiveDisplays() int
I cannot find a library to do this. Stable cross-platform screen-capturing requires a lot of work. Screen capturing requires interfacing with the operating systems' display manager/server or frame-buffer, which is different for a lot of operating systems and Linux distributions. You would have to write interfaces for each OS API (or wrap the libraries that provide the functionality), and then abstract all the different methods in a single package so it works cross-platform.
Another way to do this would be to run a existing screen capture application (command-line) to do the screen-capture work for you, including saving to a file. Then read the file in your go application. To make a Go application run a third-party application, use the os/exec package, it is in the standard library. For Linux you might use fbgrab to save the frame-buffer to a png file.
It is. It's a 2-step process:
Study https://github.com/ShareX/ShareX/tree/master/ShareX.ScreenCaptureLib to see which win32 API calls to make to capture the screen/window
Translate that logic to Go. You can use one of few existing win32 api Go bindings (e.g. https://github.com/AllenDang/w32). If they're missing needed functionality, you can add more wrappers.
For a native Windows solution, there is an example in C from the official Windows docs:
https://learn.microsoft.com/en-gb/windows/win32/gdi/capturing-an-image
Now, from this example, this is the code using Windigo library:
package main
import (
"runtime"
"unsafe"
"github.com/rodrigocfd/windigo/win"
"github.com/rodrigocfd/windigo/win/co"
)
func main() {
runtime.LockOSThread()
cxScreen := win.GetSystemMetrics(co.SM_CXSCREEN)
cyScreen := win.GetSystemMetrics(co.SM_CYSCREEN)
hdcScreen := win.HWND(0).GetDC()
defer win.HWND(0).ReleaseDC(hdcScreen)
hBmp := hdcScreen.CreateCompatibleBitmap(cxScreen, cyScreen)
defer hBmp.DeleteObject()
hdcMem := hdcScreen.CreateCompatibleDC()
defer hdcMem.DeleteDC()
hBmpOld := hdcMem.SelectObjectBitmap(hBmp)
defer hdcMem.SelectObjectBitmap(hBmpOld)
hdcMem.BitBlt(
win.POINT{X: 0, Y: 0},
win.SIZE{Cx: cxScreen, Cy: cyScreen},
hdcScreen,
win.POINT{X: 0, Y: 0},
co.ROP_SRCCOPY,
)
bi := win.BITMAPINFO{
BmiHeader: win.BITMAPINFOHEADER{
BiWidth: cxScreen,
BiHeight: cyScreen,
BiPlanes: 1,
BiBitCount: 32,
BiCompression: co.BI_RGB,
},
}
bi.BmiHeader.SetBiSize()
bmpObj := win.BITMAP{}
hBmp.GetObject(&bmpObj)
bmpSize := bmpObj.CalcBitmapSize(bi.BmiHeader.BiBitCount)
rawMem := win.GlobalAlloc(co.GMEM_FIXED|co.GMEM_ZEROINIT, bmpSize)
defer rawMem.GlobalFree()
bmpSlice := rawMem.GlobalLock(int(bmpSize))
defer rawMem.GlobalUnlock()
hdcScreen.GetDIBits(hBmp, 0, int(cyScreen), bmpSlice, &bi, co.DIB_RGB_COLORS)
bfh := win.BITMAPFILEHEADER{}
bfh.SetBfType()
bfh.SetBfOffBits(uint32(unsafe.Sizeof(bfh) + unsafe.Sizeof(bi.BmiHeader)))
bfh.SetBfSize(bfh.BfOffBits() + uint32(bmpSize))
fo, _ := win.FileOpen("C:\\users\\rodrigo\\desktop\\a.bmp", co.FILE_OPEN_RW_OPEN_OR_CREATE)
defer fo.Close()
fo.Write(bfh.Serialize())
fo.Write(bi.BmiHeader.Serialize())
fo.Write(bmpSlice)
println("Done")
}

Resources