mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-04 12:52:06 -04:00
173 lines
4.0 KiB
Go
173 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management"
|
|
wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
|
|
)
|
|
|
|
type randrOutput struct {
|
|
Name string `json:"name"`
|
|
Scale float64 `json:"scale"`
|
|
Width int32 `json:"width"`
|
|
Height int32 `json:"height"`
|
|
Refresh int32 `json:"refresh"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
type randrHead struct {
|
|
name string
|
|
enabled bool
|
|
scale float64
|
|
currentModeID uint32
|
|
modeIDs []uint32
|
|
}
|
|
|
|
type randrMode struct {
|
|
width int32
|
|
height int32
|
|
refresh int32
|
|
}
|
|
|
|
type randrClient struct {
|
|
display *wlclient.Display
|
|
ctx *wlclient.Context
|
|
manager *wlr_output_management.ZwlrOutputManagerV1
|
|
heads map[uint32]*randrHead
|
|
modes map[uint32]*randrMode
|
|
done bool
|
|
err error
|
|
}
|
|
|
|
func queryRandr() ([]randrOutput, error) {
|
|
display, err := wlclient.Connect("")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to connect to Wayland: %w", err)
|
|
}
|
|
|
|
c := &randrClient{
|
|
display: display,
|
|
ctx: display.Context(),
|
|
heads: make(map[uint32]*randrHead),
|
|
modes: make(map[uint32]*randrMode),
|
|
}
|
|
defer c.ctx.Close()
|
|
|
|
registry, err := display.GetRegistry()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get registry: %w", err)
|
|
}
|
|
|
|
registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
|
|
if e.Interface == wlr_output_management.ZwlrOutputManagerV1InterfaceName {
|
|
mgr := wlr_output_management.NewZwlrOutputManagerV1(c.ctx)
|
|
version := min(e.Version, 4)
|
|
|
|
mgr.SetHeadHandler(func(e wlr_output_management.ZwlrOutputManagerV1HeadEvent) {
|
|
c.handleHead(e)
|
|
})
|
|
|
|
mgr.SetDoneHandler(func(e wlr_output_management.ZwlrOutputManagerV1DoneEvent) {
|
|
c.done = true
|
|
})
|
|
|
|
if err := registry.Bind(e.Name, e.Interface, version, mgr); err == nil {
|
|
c.manager = mgr
|
|
}
|
|
}
|
|
})
|
|
|
|
// First roundtrip: discover globals and bind manager
|
|
syncCallback, err := display.Sync()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to sync display: %w", err)
|
|
}
|
|
syncCallback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) {
|
|
if c.manager == nil {
|
|
c.err = fmt.Errorf("zwlr_output_manager_v1 protocol not supported by compositor")
|
|
c.done = true
|
|
}
|
|
// Otherwise wait for manager's DoneHandler
|
|
})
|
|
|
|
for !c.done {
|
|
if err := c.ctx.Dispatch(); err != nil {
|
|
return nil, fmt.Errorf("dispatch error: %w", err)
|
|
}
|
|
}
|
|
|
|
if c.err != nil {
|
|
return nil, c.err
|
|
}
|
|
|
|
return c.buildOutputs(), nil
|
|
}
|
|
|
|
func (c *randrClient) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEvent) {
|
|
handle := e.Head
|
|
headID := handle.ID()
|
|
|
|
head := &randrHead{
|
|
modeIDs: make([]uint32, 0),
|
|
}
|
|
c.heads[headID] = head
|
|
|
|
handle.SetNameHandler(func(e wlr_output_management.ZwlrOutputHeadV1NameEvent) {
|
|
head.name = e.Name
|
|
})
|
|
|
|
handle.SetEnabledHandler(func(e wlr_output_management.ZwlrOutputHeadV1EnabledEvent) {
|
|
head.enabled = e.Enabled != 0
|
|
})
|
|
|
|
handle.SetScaleHandler(func(e wlr_output_management.ZwlrOutputHeadV1ScaleEvent) {
|
|
head.scale = e.Scale
|
|
})
|
|
|
|
handle.SetCurrentModeHandler(func(e wlr_output_management.ZwlrOutputHeadV1CurrentModeEvent) {
|
|
head.currentModeID = e.Mode.ID()
|
|
})
|
|
|
|
handle.SetModeHandler(func(e wlr_output_management.ZwlrOutputHeadV1ModeEvent) {
|
|
modeHandle := e.Mode
|
|
modeID := modeHandle.ID()
|
|
|
|
head.modeIDs = append(head.modeIDs, modeID)
|
|
|
|
mode := &randrMode{}
|
|
c.modes[modeID] = mode
|
|
|
|
modeHandle.SetSizeHandler(func(e wlr_output_management.ZwlrOutputModeV1SizeEvent) {
|
|
mode.width = e.Width
|
|
mode.height = e.Height
|
|
})
|
|
|
|
modeHandle.SetRefreshHandler(func(e wlr_output_management.ZwlrOutputModeV1RefreshEvent) {
|
|
mode.refresh = e.Refresh
|
|
})
|
|
})
|
|
}
|
|
|
|
func (c *randrClient) buildOutputs() []randrOutput {
|
|
outputs := make([]randrOutput, 0, len(c.heads))
|
|
|
|
for _, head := range c.heads {
|
|
out := randrOutput{
|
|
Name: head.name,
|
|
Scale: head.scale,
|
|
Enabled: head.enabled,
|
|
}
|
|
|
|
if mode, ok := c.modes[head.currentModeID]; ok {
|
|
out.Width = mode.width
|
|
out.Height = mode.height
|
|
out.Refresh = mode.refresh
|
|
}
|
|
|
|
outputs = append(outputs, out)
|
|
}
|
|
|
|
return outputs
|
|
}
|