initial commit
This commit is contained in:
commit
200a67cd7b
3 changed files with 289 additions and 0 deletions
9
go.mod
Normal file
9
go.mod
Normal file
|
@ -0,0 +1,9 @@
|
|||
module github.com/shu-go/vvin
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b
|
||||
github.com/shu-go/gli v0.0.0-20191003020935-c6817caa1c0a
|
||||
github.com/shu-go/rog v0.0.0-20190826055139-09f31aeaaebd
|
||||
)
|
12
go.sum
Normal file
12
go.sum
Normal file
|
@ -0,0 +1,12 @@
|
|||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b h1:9+ke9YJ9KGWw5ANXK6ozjoK47uI3uNbXv4YVINBnGm8=
|
||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||
github.com/shu-go/cliparser v0.0.0-20190822025044-17b54d2ae1aa h1:/o4k5mrlZXF78Ql4IbmbQwaqGklOYMPd0uQpyGxmYLg=
|
||||
github.com/shu-go/cliparser v0.0.0-20190822025044-17b54d2ae1aa/go.mod h1:b89EpZSowgrnN/iRGjwCHlRcIMMQjOGm3NmffyFpF9M=
|
||||
github.com/shu-go/clise v0.0.0-20190822023516-79849fb81cfe/go.mod h1:VLiMEzXMBozBLD37i3id3qPflaupus48v/979ipQ43s=
|
||||
github.com/shu-go/gli v0.0.0-20191003020935-c6817caa1c0a h1:N7WMOChI8J5Dx22ZNbg4xaogju9gKARbjWmNaLo5irM=
|
||||
github.com/shu-go/gli v0.0.0-20191003020935-c6817caa1c0a/go.mod h1:F0pECDJ/7sP7e6/BlCGNScgK3O3dOLo/0d8CeH1Z7Ks=
|
||||
github.com/shu-go/gotwant v0.0.0-20190822031422-724391433f13/go.mod h1:UxvcvxZEQUBw6lS9UgXOPh1outjvx2+bvDlEOpCTuGo=
|
||||
github.com/shu-go/rog v0.0.0-20190826055139-09f31aeaaebd h1:4HEm7TeZfpMp8mrjUoRt0MxTCvJrakuRn3rSbOSOBqE=
|
||||
github.com/shu-go/rog v0.0.0-20190826055139-09f31aeaaebd/go.mod h1:z2vjwV5lUEhVBjFneBZQECuBiel1kmKuU0sVJCwysDY=
|
268
vvin.go
Normal file
268
vvin.go
Normal file
|
@ -0,0 +1,268 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/mitchellh/go-ps"
|
||||
"github.com/shu-go/gli"
|
||||
"github.com/shu-go/rog"
|
||||
)
|
||||
|
||||
type globalCmd struct {
|
||||
Target string `cli:"target,t=WINDOW_TITLE" help:"default to current window"`
|
||||
Debug bool
|
||||
|
||||
Minimize minCmd `cli:"minimize,min"`
|
||||
Maximize maxCmd `cli:"maximize,max"`
|
||||
Resize resizeCmd `cli:"resize"`
|
||||
Move moveCmd `cli:"move,mv"`
|
||||
|
||||
targetHandle syscall.Handle
|
||||
|
||||
scrWidth, scrHeight int
|
||||
}
|
||||
|
||||
func (c *globalCmd) Before() error {
|
||||
wins, err := listAllWindows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
an := ancestors()
|
||||
t := strings.ToLower(c.Target)
|
||||
|
||||
for _, w := range wins {
|
||||
ancestor := false
|
||||
for _, p := range an {
|
||||
if w.PID == p {
|
||||
ancestor = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if c.Debug {
|
||||
rog.Printf("win: %#v (ancestor? %v)", w, ancestor)
|
||||
}
|
||||
if t != "" && !ancestor {
|
||||
wt := strings.ToLower(w.Title)
|
||||
|
||||
if strings.Contains(wt, t) {
|
||||
c.targetHandle = w.Handle
|
||||
break
|
||||
}
|
||||
} else if t == "" && ancestor {
|
||||
c.targetHandle = w.Handle
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if c.targetHandle == 0 {
|
||||
return errors.New("no target")
|
||||
}
|
||||
|
||||
w, _, _ := getSystemMetrics.Call(SM_CXVIRTUALSCREEN)
|
||||
h, _, _ := getSystemMetrics.Call(SM_CYVIRTUALSCREEN)
|
||||
c.scrWidth = int(w)
|
||||
c.scrHeight = int(h)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type minCmd struct {
|
||||
Restore bool `cli:"restore,r"`
|
||||
}
|
||||
|
||||
func (c minCmd) Run(g globalCmd) {
|
||||
if c.Restore {
|
||||
showWindow.Call(uintptr(g.targetHandle), SW_RESTORE)
|
||||
} else {
|
||||
showWindow.Call(uintptr(g.targetHandle), SW_MINIMIZE)
|
||||
}
|
||||
}
|
||||
|
||||
type maxCmd struct {
|
||||
Restore bool `cli:"restore,r"`
|
||||
}
|
||||
|
||||
func (c maxCmd) Run(g globalCmd) {
|
||||
if c.Restore {
|
||||
showWindow.Call(uintptr(g.targetHandle), SW_RESTORE)
|
||||
} else {
|
||||
showWindow.Call(uintptr(g.targetHandle), SW_MAXIMIZE)
|
||||
}
|
||||
}
|
||||
|
||||
type resizeCmd struct {
|
||||
Left int `cli:"left,x"`
|
||||
Top int `cli:"top,y"`
|
||||
Width int `cli:"width,w"`
|
||||
Height int `cli:"height,h"`
|
||||
|
||||
NoRestorable bool `cli:"norestorable"`
|
||||
}
|
||||
|
||||
func (c resizeCmd) Run(g globalCmd) {
|
||||
if !c.NoRestorable {
|
||||
showWindow.Call(uintptr(g.targetHandle), SW_MAXIMIZE)
|
||||
}
|
||||
setWindowPos.Call(
|
||||
uintptr(g.targetHandle),
|
||||
0,
|
||||
uintptr(c.Left),
|
||||
uintptr(c.Top),
|
||||
uintptr(c.Width),
|
||||
uintptr(c.Height),
|
||||
SWP_NOACTIVATE|SWP_NOZORDER)
|
||||
}
|
||||
|
||||
type moveCmd struct {
|
||||
Left int `cli:"left,x"`
|
||||
Top int `cli:"top,y"`
|
||||
|
||||
NoRestorable bool `cli:"norestorable"`
|
||||
}
|
||||
|
||||
func (c moveCmd) Run(g globalCmd) {
|
||||
rect := struct {
|
||||
Left, Top, Right, Bottom int32
|
||||
}{}
|
||||
|
||||
if !c.NoRestorable {
|
||||
getWindowRect.Call(uintptr(g.targetHandle), uintptr(unsafe.Pointer(&rect)))
|
||||
showWindow.Call(uintptr(g.targetHandle), SW_MAXIMIZE)
|
||||
setWindowPos.Call(
|
||||
uintptr(g.targetHandle),
|
||||
0,
|
||||
uintptr(c.Left),
|
||||
uintptr(c.Top),
|
||||
uintptr(rect.Right-rect.Left),
|
||||
uintptr(rect.Bottom-rect.Top),
|
||||
SWP_NOACTIVATE|SWP_NOZORDER)
|
||||
} else {
|
||||
setWindowPos.Call(
|
||||
uintptr(g.targetHandle),
|
||||
0,
|
||||
uintptr(c.Left),
|
||||
uintptr(c.Top),
|
||||
0,
|
||||
0,
|
||||
SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := gli.NewWith(&globalCmd{})
|
||||
app.Name = "vvin"
|
||||
app.Desc = ""
|
||||
app.Version = "0.0.0"
|
||||
app.Usage = ``
|
||||
app.Copyright = "(C) 2019 Shuhei Kubota"
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
var (
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
enumWindows = user32.NewProc("EnumWindows")
|
||||
getWindowText = user32.NewProc("GetWindowTextW")
|
||||
getWindowTextLength = user32.NewProc("GetWindowTextLengthW")
|
||||
getWindowThreadProcessId = user32.NewProc("GetWindowThreadProcessId")
|
||||
isWindow = user32.NewProc("IsWindow")
|
||||
isWindowVisible = user32.NewProc("IsWindowVisible")
|
||||
showWindow = user32.NewProc("ShowWindow")
|
||||
setWindowPos = user32.NewProc("SetWindowPos")
|
||||
getWindowRect = user32.NewProc("GetWindowRect")
|
||||
getSystemMetrics = user32.NewProc("GetSystemMetrics")
|
||||
)
|
||||
|
||||
const (
|
||||
SW_MAXIMIZE = 3
|
||||
SW_MINIMIZE = 6
|
||||
SW_RESTORE = 9
|
||||
|
||||
SWP_NOACTIVATE = 0x0010
|
||||
SWP_NOSIZE = 0x0001
|
||||
SWP_NOZORDER = 0x0004
|
||||
|
||||
SM_CXVIRTUALSCREEN = 78
|
||||
SM_CYVIRTUALSCREEN = 79
|
||||
)
|
||||
|
||||
type (
|
||||
Window struct {
|
||||
Title string
|
||||
Handle syscall.Handle
|
||||
PID int
|
||||
}
|
||||
)
|
||||
|
||||
func listAllWindows() (wins []*Window, err error) {
|
||||
cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
|
||||
b, _, _ := isWindow.Call(uintptr(hwnd))
|
||||
if b == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
b, _, _ = isWindowVisible.Call(uintptr(hwnd))
|
||||
if b == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
title := ""
|
||||
tlen, _, _ := getWindowTextLength.Call(uintptr(hwnd))
|
||||
if tlen != 0 {
|
||||
tlen++
|
||||
buff := make([]uint16, tlen)
|
||||
getWindowText.Call(
|
||||
uintptr(hwnd),
|
||||
uintptr(unsafe.Pointer(&buff[0])),
|
||||
uintptr(tlen),
|
||||
)
|
||||
title = syscall.UTF16ToString(buff)
|
||||
}
|
||||
|
||||
var processID uintptr
|
||||
getWindowThreadProcessId.Call(
|
||||
uintptr(hwnd),
|
||||
uintptr(unsafe.Pointer(&processID)),
|
||||
)
|
||||
|
||||
win := &Window{
|
||||
Title: title,
|
||||
Handle: hwnd,
|
||||
PID: int(processID),
|
||||
}
|
||||
wins = append(wins, win)
|
||||
|
||||
return 1
|
||||
})
|
||||
|
||||
a, _, _ := enumWindows.Call(cb, 0)
|
||||
if a == 0 {
|
||||
return nil, fmt.Errorf("USER32.EnumWindows returned FALSE")
|
||||
}
|
||||
|
||||
return wins, nil
|
||||
}
|
||||
|
||||
func ancestors() []int {
|
||||
curr := os.Getpid()
|
||||
|
||||
an := []int{curr}
|
||||
|
||||
for {
|
||||
p, err := ps.FindProcess(curr)
|
||||
if p == nil || err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
curr = p.PPid()
|
||||
an = append(an, curr)
|
||||
}
|
||||
|
||||
return an
|
||||
}
|
Loading…
Reference in a new issue