Skip to content

Commit 93f8b3d

Browse files
authored
fix(hypr): allow for empty monitor description (#49)
## What does this PR do? Allow for empty description in hypr spec, thus: - freeze and profile maker need to use `.name` when `.desc` is empty ## Why is this change important? Headless displays cant be configured at the moment, see #47 . ## How to test this PR locally? `make pre-push` ## Related issues <!-- Closes #234 --> Closes #47
2 parents 06f33e0 + cefbe2d commit 93f8b3d

25 files changed

+328
-50
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ build/test:
140140
test/integration: build/test
141141
@rm -rf .coverdata
142142
@mkdir -p .coverdata
143-
@HDM_BINARY_PATH=$(TEST_EXECUTABLE_NAME) $(GOTESTSUMINTEGRATION) -- -v ./test/... --debug --count=1
143+
@HDM_BINARY_PATH=$(TEST_EXECUTABLE_NAME) $(GOTESTSUMINTEGRATION) --rerun-fails=2 --packages="./test/..." -- -v ./test/... --debug --count=1
144144
@$(GOLANG_BIN) tool covdata textfmt -i=.coverdata -o=integration.txt
145145

146146
test/integration/regenerate: build/test

docs/docs/configuration/monitor-matching.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ sidebar_position: 2
66

77
Profiles match monitors based on their properties. Understanding how monitor matching works is essential for creating effective profiles.
88

9+
:::tip
10+
For fake outputs (headless displays) the description from `hyprctl` will be empty so use the `name` matcher.
11+
:::
12+
913
## Matching Properties
1014

1115
You can match monitors by:

internal/hypr/ipc_test.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -400,23 +400,25 @@ func TestIPC_GetConnectedMonitors(t *testing.T) {
400400
description: "Should successfully parse valid monitor response",
401401
},
402402
{
403-
name: "missing_description",
404-
responseFile: "testdata/monitors_response_missing_description.json",
405-
expectedError: "failed to validate response: invalid monitor: desc cant be empty",
406-
description: "Should fail when monitor block is missing description",
403+
name: "missing_description",
404+
responseFile: "testdata/monitors_response_missing_description.json",
405+
description: "Should succeed when monitor block is missing description (this is the case for virtual/headless outputs)",
406+
expectedMonitors: []*hypr.MonitorSpec{
407+
{
408+
Name: "eDP-1",
409+
ID: utils.IntPtr(0),
410+
Description: "",
411+
SdrBrightness: 1,
412+
SdrSaturation: 1,
413+
},
414+
},
407415
},
408416
{
409417
name: "malformed_header",
410418
responseFile: "testdata/monitors_response_malformed_header.json",
411419
expectedError: "failed to validate response: invalid monitor: id cant be nil",
412420
description: "Should fail when monitor header has wrong number of parts",
413421
},
414-
{
415-
name: "malformed_description",
416-
responseFile: "testdata/monitors_response_malformed_description.json",
417-
expectedError: "failed to validate response: invalid monitor: desc cant be empty",
418-
description: "Should fail when description line has wrong format",
419-
},
420422
}
421423

422424
for _, tt := range tests {

internal/hypr/testdata/monitors_response_malformed_description.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

internal/hypr/testdata/monitors_response_missing_description.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[
22
{
3+
"description": "",
34
"id": 0,
45
"name": "eDP-1"
56
}

internal/hypr/types.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ func (m *MonitorSpec) Validate() error {
6565
if *m.ID < 0 {
6666
return errors.New("id cant < 0")
6767
}
68-
if m.Description == "" {
69-
return errors.New("desc cant be empty")
70-
}
7168
if m.Name == "" {
7269
return errors.New("name cant be empty")
7370
}
71+
if m.Description == "" {
72+
logrus.WithFields(logrus.Fields{"id": m.ID, "name": m.Name}).Warning("Monitor description is empty")
73+
}
7474
if !m.TenBitdepth {
7575
m.TenBitdepth = m.IsTenBitdepth()
7676
}

internal/profilemaker/service.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,14 @@ func (s *Service) prepare(profileName, profileFileLocation string,
268268
requiredMonitors := make([]*config.RequiredMonitor, len(currentMonitors))
269269
for i, monitor := range currentMonitors {
270270
requiredMonitors[i] = &config.RequiredMonitor{
271-
Description: &monitor.Description,
272-
MonitorTag: utils.StringPtr(fmt.Sprintf("monitor%d", *monitor.ID)),
271+
MonitorTag: utils.StringPtr(fmt.Sprintf("monitor%d", *monitor.ID)),
272+
}
273+
// eagerly match on the description to allow for port swaps
274+
if monitor.Description != "" {
275+
requiredMonitors[i].Description = &monitor.Description
276+
} else {
277+
// otherwise fallback on the name matching as that cant be empty
278+
requiredMonitors[i].Name = &monitor.Name
273279
}
274280
}
275281
profile := config.Profile{
@@ -291,7 +297,12 @@ func (s *Service) ToHyprLines(monitors hypr.MonitorSpecs) []string {
291297

292298
for _, monitor := range monitors {
293299
fields := []string{}
294-
line := "monitor=desc:" + monitor.Description
300+
301+
identifier := monitor.Name
302+
if monitor.Description != "" {
303+
identifier = "desc:" + monitor.Description
304+
}
305+
line := "monitor=" + identifier
295306
fields = append(fields, line)
296307
if monitor.Disabled {
297308
fields = append(fields, "disable")

internal/profilemaker/service_test.go

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package profilemaker_test
22

33
import (
4+
"flag"
45
"os"
56
"path/filepath"
67
"strings"
@@ -15,6 +16,8 @@ import (
1516
"github.com/stretchr/testify/require"
1617
)
1718

19+
var regenerate = flag.Bool("regenerate", false, "regenerate fixtures instead of comparing")
20+
1821
func TestService_EditExisting(t *testing.T) {
1922
// Sample monitors data
2023
monitors := []*hypr.MonitorSpec{
@@ -49,6 +52,18 @@ func TestService_EditExisting(t *testing.T) {
4952
SdrBrightness: 1.1,
5053
SdrSaturation: 0.98,
5154
},
55+
{
56+
ID: utils.IntPtr(3),
57+
Name: "monC",
58+
Description: "",
59+
Width: 1000,
60+
Height: 1000,
61+
RefreshRate: 60.0,
62+
X: -1000,
63+
Y: -1000,
64+
Scale: 1.0,
65+
Transform: 0,
66+
},
5267
}
5368

5469
for _, monitor := range monitors {
@@ -175,23 +190,7 @@ func TestService_EditExisting(t *testing.T) {
175190

176191
assert.NoError(t, err)
177192

178-
// nolint:gosec
179-
resultContent, err := os.ReadFile(configFile)
180-
require.NoError(t, err)
181-
182-
// nolint:gosec
183-
expectedContent, err := os.ReadFile(tc.expectedFile)
184-
require.NoError(t, err)
185-
186-
actualOutput := strings.TrimSpace(string(resultContent))
187-
expectedOutput := strings.TrimSpace(string(expectedContent))
188-
189-
assert.Equal(t, expectedOutput, actualOutput)
190-
191-
if !tc.expectError {
192-
assert.Contains(t, string(resultContent), "# <<<<< TUI AUTO START")
193-
assert.Contains(t, string(resultContent), "# <<<<< TUI AUTO END")
194-
}
193+
testutils.AssertFixture(t, configFile, tc.expectedFile, *regenerate)
195194
})
196195
}
197196
}

internal/profilemaker/testdata/expected_append_broken_markers.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ bind = $mainMod, Q, exec, kitty
99
# <<<<< TUI AUTO START
1010
monitor=desc:New Monitor A,2560x1440@120.00000,0x0,1.50,transform,0,vrr,0
1111
monitor=desc:New Monitor B,1920x1080@60.00000,2560x0,1.00,transform,0,vrr,1,bitdepth,10,cm,hdr,sdrbrightness,1.10,sdrsaturation,0.98,mirror,eDP-1
12+
monitor=monC,1000x1000@60.00000,-1000x-1000,1.00,transform,0,vrr,0
1213
# <<<<< TUI AUTO END

internal/profilemaker/testdata/expected_append_no_markers.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ bind = $mainMod, C, killactive
77
# <<<<< TUI AUTO START
88
monitor=desc:New Monitor A,2560x1440@120.00000,0x0,1.50,transform,0,vrr,0
99
monitor=desc:New Monitor B,1920x1080@60.00000,2560x0,1.00,transform,0,vrr,1,bitdepth,10,cm,hdr,sdrbrightness,1.10,sdrsaturation,0.98,mirror,eDP-1
10+
monitor=monC,1000x1000@60.00000,-1000x-1000,1.00,transform,0,vrr,0
1011
# <<<<< TUI AUTO END

0 commit comments

Comments
 (0)