hypr-fixwinbounds/main.go

110 lines
2.7 KiB
Go
Raw Normal View History

2024-04-05 13:40:10 +02:00
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 {
2024-04-05 13:49:20 +02:00
// ignore all special workspaces because they can't be moved between monitors
2024-04-05 13:40:10 +02:00
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])
2024-04-05 13:49:20 +02:00
monitorBounds := monitorBoundsByID[window.MonitorID]
if windowBounds.In(monitorBounds) {
2024-04-05 13:40:10 +02:00
continue
}
2024-04-05 13:54:28 +02:00
boundsOfActualMonitor := GetMonitorBoundsByWindowBounds(windowBounds, monitorBoundsByID)
2024-04-05 13:40:10 +02:00
2024-04-05 13:49:20 +02:00
// 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)
2024-04-05 13:40:10 +02:00
2024-04-05 13:49:20 +02:00
// use coords [0,0] respective to the wanted monitor
// if window is completely OOB
if boundsOfActualMonitor == image.Rect(-1, -1, -1, -1) {
shouldPos = monitorBounds.Min
2024-04-05 13:40:10 +02:00
}
2024-04-05 13:49:20 +02:00
cmd := fmt.Sprintf(
2024-04-05 13:40:10 +02:00
"movewindowpixel exact %d %d,address:%s",
shouldPos.X,
shouldPos.Y,
2024-04-05 13:49:20 +02:00
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
}
2024-04-05 13:40:10 +02:00
log.Printf("invalid position restored for window (class: '%s' | title: '%s')\n", window.Class, window.Title)
}
return nil
}
2024-04-05 13:54:28 +02:00
// 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) {
return monitorBounds
}
}
return image.Rect(-1, -1, -1, -1)
}