111 lines
2.7 KiB
Go
111 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"image"
|
|
"log"
|
|
|
|
"git.milar.in/milarin/hypr"
|
|
"git.milar.in/milarin/slices"
|
|
)
|
|
|
|
func main() {
|
|
client, err := hypr.GetDefaultClient()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
events, err := client.Subscribe(context.Background(), hypr.EventTypeFullscreen)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := RestoreWindowPositions(client); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
for event := range events {
|
|
if len(event.Data) != 1 || event.Data[0] != "0" {
|
|
continue
|
|
}
|
|
|
|
if err := RestoreWindowPositions(client); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func RestoreWindowPositions(client *hypr.Client) error {
|
|
monitors, err := client.GetMonitors()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
monitorBoundsByID := slices.ToMap(monitors, func(m *hypr.Monitor) (int, image.Rectangle) {
|
|
return m.ID, image.Rect(m.X, m.Y, m.X+m.Width, m.Y+m.Height)
|
|
})
|
|
|
|
windows, err := client.GetWindows()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, window := range windows {
|
|
// ignore all special workspaces because they can't be moved between monitors
|
|
if window.Workspace.ID < 0 {
|
|
continue
|
|
}
|
|
|
|
windowBounds := image.Rect(
|
|
window.At[0],
|
|
window.At[1],
|
|
window.At[0]+window.Size[0],
|
|
window.At[1]+window.Size[1])
|
|
|
|
monitorBounds := monitorBoundsByID[window.MonitorID]
|
|
if windowBounds.In(monitorBounds) {
|
|
continue
|
|
}
|
|
|
|
boundsOfActualMonitor := GetMonitorBoundsByWindowBounds(windowBounds, monitorBoundsByID)
|
|
|
|
shouldPos := image.Pt(-1, -1)
|
|
if boundsOfActualMonitor == nil {
|
|
// use coords [0,0] respective to the wanted monitor if window is completely OOB
|
|
shouldPos = monitorBounds.Min
|
|
} else {
|
|
// calculate new position based on the position of the window and its actual monitor
|
|
diff := windowBounds.Min.Sub(boundsOfActualMonitor.Min)
|
|
shouldPos = monitorBounds.Min.Add(diff)
|
|
}
|
|
|
|
cmd := fmt.Sprintf(
|
|
"movewindowpixel exact %d %d,address:%s",
|
|
shouldPos.X,
|
|
shouldPos.Y,
|
|
window.Address)
|
|
|
|
if err := client.DispatchExpectOK(cmd); err != nil {
|
|
log.Printf("invalid position could NOT be restored successfully for window (class: '%s' | title: '%s')\n", window.Class, window.Title)
|
|
continue
|
|
}
|
|
|
|
log.Printf("invalid position restored for window (class: '%s' | title: '%s')\n", window.Class, window.Title)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetMonitorBoundsByWindowBounds returns the monitor bounds which contains windowBounds
|
|
// or image.Rect(-1, -1, -1, -1) if windowBounds is outside any monitor bounds
|
|
func GetMonitorBoundsByWindowBounds(windowBounds image.Rectangle, monitorBoundsByID map[int]image.Rectangle) *image.Rectangle {
|
|
for _, monitorBounds := range monitorBoundsByID {
|
|
if windowBounds.In(monitorBounds) {
|
|
newBounds := windowBounds
|
|
return &newBounds
|
|
}
|
|
}
|
|
return nil
|
|
}
|