summaryrefslogtreecommitdiff
path: root/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules')
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser.go658
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser_test.go280
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser.go875
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser_test.go335
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/providers_test.go22
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/types.go103
6 files changed, 0 insertions, 2273 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser.go b/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser.go
deleted file mode 100644
index 918c4f9..0000000
--- a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser.go
+++ /dev/null
@@ -1,658 +0,0 @@
-package providers
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/windowrules"
-)
-
-type HyprlandWindowRule struct {
- MatchClass string
- MatchTitle string
- MatchXWayland *bool
- MatchFloating *bool
- MatchFullscreen *bool
- MatchPinned *bool
- MatchInitialised *bool
- Rule string
- Value string
- Source string
- RawLine string
-}
-
-type HyprlandRulesParser struct {
- configDir string
- processedFiles map[string]bool
- rules []HyprlandWindowRule
- currentSource string
- dmsRulesExists bool
- dmsRulesIncluded bool
- includeCount int
- dmsIncludePos int
- rulesAfterDMS int
- dmsProcessed bool
-}
-
-func NewHyprlandRulesParser(configDir string) *HyprlandRulesParser {
- return &HyprlandRulesParser{
- configDir: configDir,
- processedFiles: make(map[string]bool),
- rules: []HyprlandWindowRule{},
- dmsIncludePos: -1,
- }
-}
-
-func (p *HyprlandRulesParser) Parse() ([]HyprlandWindowRule, error) {
- expandedDir, err := utils.ExpandPath(p.configDir)
- if err != nil {
- return nil, err
- }
-
- dmsRulesPath := filepath.Join(expandedDir, "dms", "windowrules.conf")
- if _, err := os.Stat(dmsRulesPath); err == nil {
- p.dmsRulesExists = true
- }
-
- mainConfig := filepath.Join(expandedDir, "hyprland.conf")
- if err := p.parseFile(mainConfig); err != nil {
- return nil, err
- }
-
- if p.dmsRulesExists && !p.dmsProcessed {
- p.parseDMSRulesDirectly(dmsRulesPath)
- }
-
- return p.rules, nil
-}
-
-func (p *HyprlandRulesParser) parseDMSRulesDirectly(dmsRulesPath string) {
- data, err := os.ReadFile(dmsRulesPath)
- if err != nil {
- return
- }
-
- prevSource := p.currentSource
- p.currentSource = dmsRulesPath
-
- lines := strings.Split(string(data), "\n")
- for _, line := range lines {
- p.parseLine(line)
- }
-
- p.currentSource = prevSource
- p.dmsProcessed = true
-}
-
-func (p *HyprlandRulesParser) parseFile(filePath string) error {
- absPath, err := filepath.Abs(filePath)
- if err != nil {
- return err
- }
-
- if p.processedFiles[absPath] {
- return nil
- }
- p.processedFiles[absPath] = true
-
- data, err := os.ReadFile(absPath)
- if err != nil {
- return nil
- }
-
- prevSource := p.currentSource
- p.currentSource = absPath
-
- lines := strings.Split(string(data), "\n")
- for _, line := range lines {
- trimmed := strings.TrimSpace(line)
-
- if strings.HasPrefix(trimmed, "source") {
- p.handleSource(trimmed, filepath.Dir(absPath))
- continue
- }
-
- p.parseLine(line)
- }
-
- p.currentSource = prevSource
- return nil
-}
-
-func (p *HyprlandRulesParser) handleSource(line string, baseDir string) {
- parts := strings.SplitN(line, "=", 2)
- if len(parts) < 2 {
- return
- }
-
- sourcePath := strings.TrimSpace(parts[1])
- isDMSSource := sourcePath == "dms/windowrules.conf" || strings.HasSuffix(sourcePath, "/dms/windowrules.conf")
-
- p.includeCount++
- if isDMSSource {
- p.dmsRulesIncluded = true
- p.dmsIncludePos = p.includeCount
- p.dmsProcessed = true
- }
-
- fullPath := sourcePath
- if !filepath.IsAbs(sourcePath) {
- fullPath = filepath.Join(baseDir, sourcePath)
- }
-
- expanded, err := utils.ExpandPath(fullPath)
- if err != nil {
- return
- }
-
- _ = p.parseFile(expanded)
-}
-
-func (p *HyprlandRulesParser) parseLine(line string) {
- trimmed := strings.TrimSpace(line)
-
- if strings.HasPrefix(trimmed, "windowrule") {
- rule := p.parseWindowRuleLine(trimmed)
- if rule != nil {
- rule.Source = p.currentSource
- p.rules = append(p.rules, *rule)
- }
- }
-}
-
-var windowRuleV2Regex = regexp.MustCompile(`^windowrulev?2?\s*=\s*(.+)$`)
-
-func (p *HyprlandRulesParser) parseWindowRuleLine(line string) *HyprlandWindowRule {
- matches := windowRuleV2Regex.FindStringSubmatch(line)
- if len(matches) < 2 {
- return nil
- }
-
- content := strings.TrimSpace(matches[1])
- isV2 := strings.HasPrefix(line, "windowrulev2")
-
- rule := &HyprlandWindowRule{
- RawLine: line,
- }
-
- if isV2 {
- p.parseWindowRuleV2(content, rule)
- } else {
- p.parseWindowRuleV1(content, rule)
- }
-
- return rule
-}
-
-func (p *HyprlandRulesParser) parseWindowRuleV1(content string, rule *HyprlandWindowRule) {
- parts := strings.SplitN(content, ",", 2)
- if len(parts) < 2 {
- return
- }
-
- rule.Rule = strings.TrimSpace(parts[0])
- rule.MatchClass = strings.TrimSpace(parts[1])
-}
-
-func (p *HyprlandRulesParser) parseWindowRuleV2(content string, rule *HyprlandWindowRule) {
- parts := strings.SplitN(content, ",", 2)
- if len(parts) < 2 {
- return
- }
-
- ruleAndValue := strings.TrimSpace(parts[0])
- matchPart := strings.TrimSpace(parts[1])
-
- if idx := strings.Index(ruleAndValue, " "); idx > 0 {
- rule.Rule = ruleAndValue[:idx]
- rule.Value = strings.TrimSpace(ruleAndValue[idx+1:])
- } else {
- rule.Rule = ruleAndValue
- }
-
- matchPairs := strings.Split(matchPart, ",")
- for _, pair := range matchPairs {
- pair = strings.TrimSpace(pair)
- if colonIdx := strings.Index(pair, ":"); colonIdx > 0 {
- key := strings.TrimSpace(pair[:colonIdx])
- value := strings.TrimSpace(pair[colonIdx+1:])
-
- switch key {
- case "class":
- rule.MatchClass = value
- case "title":
- rule.MatchTitle = value
- case "xwayland":
- b := value == "1" || value == "true"
- rule.MatchXWayland = &b
- case "floating":
- b := value == "1" || value == "true"
- rule.MatchFloating = &b
- case "fullscreen":
- b := value == "1" || value == "true"
- rule.MatchFullscreen = &b
- case "pinned":
- b := value == "1" || value == "true"
- rule.MatchPinned = &b
- case "initialised", "initialized":
- b := value == "1" || value == "true"
- rule.MatchInitialised = &b
- }
- }
- }
-}
-
-func (p *HyprlandRulesParser) HasDMSRulesIncluded() bool {
- return p.dmsRulesIncluded
-}
-
-func (p *HyprlandRulesParser) buildDMSStatus() *windowrules.DMSRulesStatus {
- status := &windowrules.DMSRulesStatus{
- Exists: p.dmsRulesExists,
- Included: p.dmsRulesIncluded,
- IncludePosition: p.dmsIncludePos,
- TotalIncludes: p.includeCount,
- RulesAfterDMS: p.rulesAfterDMS,
- }
-
- switch {
- case !p.dmsRulesExists:
- status.Effective = false
- status.StatusMessage = "dms/windowrules.conf does not exist"
- case !p.dmsRulesIncluded:
- status.Effective = false
- status.StatusMessage = "dms/windowrules.conf is not sourced in config"
- case p.rulesAfterDMS > 0:
- status.Effective = true
- status.OverriddenBy = p.rulesAfterDMS
- status.StatusMessage = "Some DMS rules may be overridden by config rules"
- default:
- status.Effective = true
- status.StatusMessage = "DMS window rules are active"
- }
-
- return status
-}
-
-type HyprlandRulesParseResult struct {
- Rules []HyprlandWindowRule
- DMSRulesIncluded bool
- DMSStatus *windowrules.DMSRulesStatus
-}
-
-func ParseHyprlandWindowRules(configDir string) (*HyprlandRulesParseResult, error) {
- parser := NewHyprlandRulesParser(configDir)
- rules, err := parser.Parse()
- if err != nil {
- return nil, err
- }
- return &HyprlandRulesParseResult{
- Rules: rules,
- DMSRulesIncluded: parser.HasDMSRulesIncluded(),
- DMSStatus: parser.buildDMSStatus(),
- }, nil
-}
-
-func applyHyprlandRuleAction(actions *windowrules.Actions, rule, value string) {
- t := true
- switch rule {
- case "float":
- actions.OpenFloating = &t
- case "tile":
- actions.Tile = &t
- case "fullscreen":
- actions.OpenFullscreen = &t
- case "maximize":
- actions.OpenMaximized = &t
- case "nofocus":
- actions.NoFocus = &t
- case "noborder":
- actions.NoBorder = &t
- case "noshadow":
- actions.NoShadow = &t
- case "nodim":
- actions.NoDim = &t
- case "noblur":
- actions.NoBlur = &t
- case "noanim":
- actions.NoAnim = &t
- case "norounding":
- actions.NoRounding = &t
- case "pin":
- actions.Pin = &t
- case "opaque":
- actions.Opaque = &t
- case "forcergbx":
- actions.ForcergbX = &t
- case "opacity":
- if f, err := strconv.ParseFloat(value, 64); err == nil {
- actions.Opacity = &f
- }
- case "size":
- actions.Size = value
- case "move":
- actions.Move = value
- case "monitor":
- actions.Monitor = value
- case "workspace":
- actions.Workspace = value
- case "idleinhibit":
- actions.Idleinhibit = value
- case "rounding":
- if i, err := strconv.Atoi(value); err == nil {
- actions.CornerRadius = &i
- }
- }
-}
-
-func ConvertHyprlandRulesToWindowRules(hyprRules []HyprlandWindowRule) []windowrules.WindowRule {
- result := make([]windowrules.WindowRule, 0, len(hyprRules))
- for i, hr := range hyprRules {
- wr := windowrules.WindowRule{
- ID: strconv.Itoa(i),
- Enabled: true,
- Source: hr.Source,
- MatchCriteria: windowrules.MatchCriteria{
- AppID: hr.MatchClass,
- Title: hr.MatchTitle,
- XWayland: hr.MatchXWayland,
- IsFloating: hr.MatchFloating,
- Fullscreen: hr.MatchFullscreen,
- Pinned: hr.MatchPinned,
- Initialised: hr.MatchInitialised,
- },
- }
- applyHyprlandRuleAction(&wr.Actions, hr.Rule, hr.Value)
- result = append(result, wr)
- }
- return result
-}
-
-type HyprlandWritableProvider struct {
- configDir string
-}
-
-func NewHyprlandWritableProvider(configDir string) *HyprlandWritableProvider {
- return &HyprlandWritableProvider{configDir: configDir}
-}
-
-func (p *HyprlandWritableProvider) Name() string {
- return "hyprland"
-}
-
-func (p *HyprlandWritableProvider) GetOverridePath() string {
- expanded, _ := utils.ExpandPath(p.configDir)
- return filepath.Join(expanded, "dms", "windowrules.conf")
-}
-
-func (p *HyprlandWritableProvider) GetRuleSet() (*windowrules.RuleSet, error) {
- result, err := ParseHyprlandWindowRules(p.configDir)
- if err != nil {
- return nil, err
- }
- return &windowrules.RuleSet{
- Title: "Hyprland Window Rules",
- Provider: "hyprland",
- Rules: ConvertHyprlandRulesToWindowRules(result.Rules),
- DMSRulesIncluded: result.DMSRulesIncluded,
- DMSStatus: result.DMSStatus,
- }, nil
-}
-
-func (p *HyprlandWritableProvider) SetRule(rule windowrules.WindowRule) error {
- rules, err := p.LoadDMSRules()
- if err != nil {
- rules = []windowrules.WindowRule{}
- }
-
- found := false
- for i, r := range rules {
- if r.ID == rule.ID {
- rules[i] = rule
- found = true
- break
- }
- }
- if !found {
- rules = append(rules, rule)
- }
-
- return p.writeDMSRules(rules)
-}
-
-func (p *HyprlandWritableProvider) RemoveRule(id string) error {
- rules, err := p.LoadDMSRules()
- if err != nil {
- return err
- }
-
- newRules := make([]windowrules.WindowRule, 0, len(rules))
- for _, r := range rules {
- if r.ID != id {
- newRules = append(newRules, r)
- }
- }
-
- return p.writeDMSRules(newRules)
-}
-
-func (p *HyprlandWritableProvider) ReorderRules(ids []string) error {
- rules, err := p.LoadDMSRules()
- if err != nil {
- return err
- }
-
- ruleMap := make(map[string]windowrules.WindowRule)
- for _, r := range rules {
- ruleMap[r.ID] = r
- }
-
- newRules := make([]windowrules.WindowRule, 0, len(ids))
- for _, id := range ids {
- if r, ok := ruleMap[id]; ok {
- newRules = append(newRules, r)
- delete(ruleMap, id)
- }
- }
-
- for _, r := range ruleMap {
- newRules = append(newRules, r)
- }
-
- return p.writeDMSRules(newRules)
-}
-
-var dmsRuleCommentRegex = regexp.MustCompile(`^#\s*DMS-RULE:\s*id=([^,]+),\s*name=(.*)$`)
-
-func (p *HyprlandWritableProvider) LoadDMSRules() ([]windowrules.WindowRule, error) {
- rulesPath := p.GetOverridePath()
- data, err := os.ReadFile(rulesPath)
- if err != nil {
- if os.IsNotExist(err) {
- return []windowrules.WindowRule{}, nil
- }
- return nil, err
- }
-
- var rules []windowrules.WindowRule
- var currentID, currentName string
- lines := strings.Split(string(data), "\n")
-
- for _, line := range lines {
- trimmed := strings.TrimSpace(line)
-
- if matches := dmsRuleCommentRegex.FindStringSubmatch(trimmed); matches != nil {
- currentID = matches[1]
- currentName = matches[2]
- continue
- }
-
- if strings.HasPrefix(trimmed, "windowrulev2") {
- parser := NewHyprlandRulesParser(p.configDir)
- hrule := parser.parseWindowRuleLine(trimmed)
- if hrule == nil {
- continue
- }
-
- wr := windowrules.WindowRule{
- ID: currentID,
- Name: currentName,
- Enabled: true,
- Source: rulesPath,
- MatchCriteria: windowrules.MatchCriteria{
- AppID: hrule.MatchClass,
- Title: hrule.MatchTitle,
- XWayland: hrule.MatchXWayland,
- IsFloating: hrule.MatchFloating,
- Fullscreen: hrule.MatchFullscreen,
- Pinned: hrule.MatchPinned,
- Initialised: hrule.MatchInitialised,
- },
- }
- applyHyprlandRuleAction(&wr.Actions, hrule.Rule, hrule.Value)
-
- if wr.ID == "" {
- wr.ID = hrule.MatchClass
- if wr.ID == "" {
- wr.ID = hrule.MatchTitle
- }
- }
-
- rules = append(rules, wr)
- currentID = ""
- currentName = ""
- }
- }
-
- return rules, nil
-}
-
-func (p *HyprlandWritableProvider) writeDMSRules(rules []windowrules.WindowRule) error {
- rulesPath := p.GetOverridePath()
-
- if err := os.MkdirAll(filepath.Dir(rulesPath), 0755); err != nil {
- return err
- }
-
- var lines []string
- lines = append(lines, "# DMS Window Rules - Managed by DankMaterialShell")
- lines = append(lines, "# Do not edit manually - changes may be overwritten")
- lines = append(lines, "")
-
- for _, rule := range rules {
- lines = append(lines, p.formatRuleLines(rule)...)
- }
-
- return os.WriteFile(rulesPath, []byte(strings.Join(lines, "\n")), 0644)
-}
-
-func (p *HyprlandWritableProvider) formatRuleLines(rule windowrules.WindowRule) []string {
- var lines []string
- lines = append(lines, fmt.Sprintf("# DMS-RULE: id=%s, name=%s", rule.ID, rule.Name))
-
- var matchParts []string
- if rule.MatchCriteria.AppID != "" {
- matchParts = append(matchParts, fmt.Sprintf("class:%s", rule.MatchCriteria.AppID))
- }
- if rule.MatchCriteria.Title != "" {
- matchParts = append(matchParts, fmt.Sprintf("title:%s", rule.MatchCriteria.Title))
- }
- if rule.MatchCriteria.XWayland != nil {
- matchParts = append(matchParts, fmt.Sprintf("xwayland:%d", boolToInt(*rule.MatchCriteria.XWayland)))
- }
- if rule.MatchCriteria.IsFloating != nil {
- matchParts = append(matchParts, fmt.Sprintf("floating:%d", boolToInt(*rule.MatchCriteria.IsFloating)))
- }
- if rule.MatchCriteria.Fullscreen != nil {
- matchParts = append(matchParts, fmt.Sprintf("fullscreen:%d", boolToInt(*rule.MatchCriteria.Fullscreen)))
- }
- if rule.MatchCriteria.Pinned != nil {
- matchParts = append(matchParts, fmt.Sprintf("pinned:%d", boolToInt(*rule.MatchCriteria.Pinned)))
- }
-
- matchStr := strings.Join(matchParts, ", ")
- a := rule.Actions
-
- if a.OpenFloating != nil && *a.OpenFloating {
- lines = append(lines, fmt.Sprintf("windowrulev2 = float, %s", matchStr))
- }
- if a.Tile != nil && *a.Tile {
- lines = append(lines, fmt.Sprintf("windowrulev2 = tile, %s", matchStr))
- }
- if a.OpenFullscreen != nil && *a.OpenFullscreen {
- lines = append(lines, fmt.Sprintf("windowrulev2 = fullscreen, %s", matchStr))
- }
- if a.OpenMaximized != nil && *a.OpenMaximized {
- lines = append(lines, fmt.Sprintf("windowrulev2 = maximize, %s", matchStr))
- }
- if a.NoFocus != nil && *a.NoFocus {
- lines = append(lines, fmt.Sprintf("windowrulev2 = nofocus, %s", matchStr))
- }
- if a.NoBorder != nil && *a.NoBorder {
- lines = append(lines, fmt.Sprintf("windowrulev2 = noborder, %s", matchStr))
- }
- if a.NoShadow != nil && *a.NoShadow {
- lines = append(lines, fmt.Sprintf("windowrulev2 = noshadow, %s", matchStr))
- }
- if a.NoDim != nil && *a.NoDim {
- lines = append(lines, fmt.Sprintf("windowrulev2 = nodim, %s", matchStr))
- }
- if a.NoBlur != nil && *a.NoBlur {
- lines = append(lines, fmt.Sprintf("windowrulev2 = noblur, %s", matchStr))
- }
- if a.NoAnim != nil && *a.NoAnim {
- lines = append(lines, fmt.Sprintf("windowrulev2 = noanim, %s", matchStr))
- }
- if a.NoRounding != nil && *a.NoRounding {
- lines = append(lines, fmt.Sprintf("windowrulev2 = norounding, %s", matchStr))
- }
- if a.Pin != nil && *a.Pin {
- lines = append(lines, fmt.Sprintf("windowrulev2 = pin, %s", matchStr))
- }
- if a.Opaque != nil && *a.Opaque {
- lines = append(lines, fmt.Sprintf("windowrulev2 = opaque, %s", matchStr))
- }
- if a.ForcergbX != nil && *a.ForcergbX {
- lines = append(lines, fmt.Sprintf("windowrulev2 = forcergbx, %s", matchStr))
- }
- if a.Opacity != nil {
- lines = append(lines, fmt.Sprintf("windowrulev2 = opacity %.2f, %s", *a.Opacity, matchStr))
- }
- if a.Size != "" {
- lines = append(lines, fmt.Sprintf("windowrulev2 = size %s, %s", a.Size, matchStr))
- }
- if a.Move != "" {
- lines = append(lines, fmt.Sprintf("windowrulev2 = move %s, %s", a.Move, matchStr))
- }
- if a.Monitor != "" {
- lines = append(lines, fmt.Sprintf("windowrulev2 = monitor %s, %s", a.Monitor, matchStr))
- }
- if a.Workspace != "" {
- lines = append(lines, fmt.Sprintf("windowrulev2 = workspace %s, %s", a.Workspace, matchStr))
- }
- if a.CornerRadius != nil {
- lines = append(lines, fmt.Sprintf("windowrulev2 = rounding %d, %s", *a.CornerRadius, matchStr))
- }
- if a.Idleinhibit != "" {
- lines = append(lines, fmt.Sprintf("windowrulev2 = idleinhibit %s, %s", a.Idleinhibit, matchStr))
- }
-
- if len(lines) == 1 {
- lines = append(lines, fmt.Sprintf("# (no actions defined for rule %s)", rule.ID))
- }
-
- lines = append(lines, "")
- return lines
-}
-
-func boolToInt(b bool) int {
- if b {
- return 1
- }
- return 0
-}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser_test.go b/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser_test.go
deleted file mode 100644
index 5341b4b..0000000
--- a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser_test.go
+++ /dev/null
@@ -1,280 +0,0 @@
-package providers
-
-import (
- "os"
- "path/filepath"
- "testing"
-)
-
-func TestParseWindowRuleV1(t *testing.T) {
- parser := NewHyprlandRulesParser("")
-
- tests := []struct {
- name string
- line string
- wantClass string
- wantRule string
- wantNil bool
- }{
- {
- name: "basic float rule",
- line: "windowrule = float, ^(firefox)$",
- wantClass: "^(firefox)$",
- wantRule: "float",
- },
- {
- name: "tile rule",
- line: "windowrule = tile, steam",
- wantClass: "steam",
- wantRule: "tile",
- },
- {
- name: "no match returns empty class",
- line: "windowrule = float",
- wantClass: "",
- wantRule: "",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := parser.parseWindowRuleLine(tt.line)
- if tt.wantNil {
- if result != nil {
- t.Errorf("expected nil, got %+v", result)
- }
- return
- }
- if result == nil {
- t.Fatal("expected non-nil result")
- }
- if result.MatchClass != tt.wantClass {
- t.Errorf("MatchClass = %q, want %q", result.MatchClass, tt.wantClass)
- }
- if result.Rule != tt.wantRule {
- t.Errorf("Rule = %q, want %q", result.Rule, tt.wantRule)
- }
- })
- }
-}
-
-func TestParseWindowRuleV2(t *testing.T) {
- parser := NewHyprlandRulesParser("")
-
- tests := []struct {
- name string
- line string
- wantClass string
- wantTitle string
- wantRule string
- wantValue string
- }{
- {
- name: "float with class",
- line: "windowrulev2 = float, class:^(firefox)$",
- wantClass: "^(firefox)$",
- wantRule: "float",
- },
- {
- name: "opacity with value",
- line: "windowrulev2 = opacity 0.8, class:^(code)$",
- wantClass: "^(code)$",
- wantRule: "opacity",
- wantValue: "0.8",
- },
- {
- name: "size with value and title",
- line: "windowrulev2 = size 800 600, class:^(steam)$, title:Settings",
- wantClass: "^(steam)$",
- wantTitle: "Settings",
- wantRule: "size",
- wantValue: "800 600",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := parser.parseWindowRuleLine(tt.line)
- if result == nil {
- t.Fatal("expected non-nil result")
- }
- if result.MatchClass != tt.wantClass {
- t.Errorf("MatchClass = %q, want %q", result.MatchClass, tt.wantClass)
- }
- if result.MatchTitle != tt.wantTitle {
- t.Errorf("MatchTitle = %q, want %q", result.MatchTitle, tt.wantTitle)
- }
- if result.Rule != tt.wantRule {
- t.Errorf("Rule = %q, want %q", result.Rule, tt.wantRule)
- }
- if result.Value != tt.wantValue {
- t.Errorf("Value = %q, want %q", result.Value, tt.wantValue)
- }
- })
- }
-}
-
-func TestConvertHyprlandRulesToWindowRules(t *testing.T) {
- hyprRules := []HyprlandWindowRule{
- {MatchClass: "^(firefox)$", Rule: "float"},
- {MatchClass: "^(code)$", Rule: "opacity", Value: "0.9"},
- {MatchClass: "^(steam)$", Rule: "maximize"},
- }
-
- result := ConvertHyprlandRulesToWindowRules(hyprRules)
-
- if len(result) != 3 {
- t.Errorf("expected 3 rules, got %d", len(result))
- }
-
- if result[0].MatchCriteria.AppID != "^(firefox)$" {
- t.Errorf("rule 0 AppID = %q, want ^(firefox)$", result[0].MatchCriteria.AppID)
- }
- if result[0].Actions.OpenFloating == nil || !*result[0].Actions.OpenFloating {
- t.Error("rule 0 should have OpenFloating = true")
- }
-
- if result[1].Actions.Opacity == nil || *result[1].Actions.Opacity != 0.9 {
- t.Errorf("rule 1 Opacity = %v, want 0.9", result[1].Actions.Opacity)
- }
-
- if result[2].Actions.OpenMaximized == nil || !*result[2].Actions.OpenMaximized {
- t.Error("rule 2 should have OpenMaximized = true")
- }
-}
-
-func TestHyprlandWritableProvider(t *testing.T) {
- tmpDir := t.TempDir()
- provider := NewHyprlandWritableProvider(tmpDir)
-
- if provider.Name() != "hyprland" {
- t.Errorf("Name() = %q, want hyprland", provider.Name())
- }
-
- expectedPath := filepath.Join(tmpDir, "dms", "windowrules.conf")
- if provider.GetOverridePath() != expectedPath {
- t.Errorf("GetOverridePath() = %q, want %q", provider.GetOverridePath(), expectedPath)
- }
-}
-
-func TestHyprlandSetAndLoadDMSRules(t *testing.T) {
- tmpDir := t.TempDir()
- provider := NewHyprlandWritableProvider(tmpDir)
-
- rule := newTestWindowRule("test_id", "Test Rule", "^(firefox)$")
- rule.Actions.OpenFloating = boolPtr(true)
-
- if err := provider.SetRule(rule); err != nil {
- t.Fatalf("SetRule failed: %v", err)
- }
-
- rules, err := provider.LoadDMSRules()
- if err != nil {
- t.Fatalf("LoadDMSRules failed: %v", err)
- }
-
- if len(rules) != 1 {
- t.Fatalf("expected 1 rule, got %d", len(rules))
- }
-
- if rules[0].ID != "test_id" {
- t.Errorf("ID = %q, want test_id", rules[0].ID)
- }
- if rules[0].MatchCriteria.AppID != "^(firefox)$" {
- t.Errorf("AppID = %q, want ^(firefox)$", rules[0].MatchCriteria.AppID)
- }
-}
-
-func TestHyprlandRemoveRule(t *testing.T) {
- tmpDir := t.TempDir()
- provider := NewHyprlandWritableProvider(tmpDir)
-
- rule1 := newTestWindowRule("rule1", "Rule 1", "^(app1)$")
- rule1.Actions.OpenFloating = boolPtr(true)
- rule2 := newTestWindowRule("rule2", "Rule 2", "^(app2)$")
- rule2.Actions.OpenFloating = boolPtr(true)
-
- _ = provider.SetRule(rule1)
- _ = provider.SetRule(rule2)
-
- if err := provider.RemoveRule("rule1"); err != nil {
- t.Fatalf("RemoveRule failed: %v", err)
- }
-
- rules, _ := provider.LoadDMSRules()
- if len(rules) != 1 {
- t.Fatalf("expected 1 rule after removal, got %d", len(rules))
- }
- if rules[0].ID != "rule2" {
- t.Errorf("remaining rule ID = %q, want rule2", rules[0].ID)
- }
-}
-
-func TestHyprlandReorderRules(t *testing.T) {
- tmpDir := t.TempDir()
- provider := NewHyprlandWritableProvider(tmpDir)
-
- rule1 := newTestWindowRule("rule1", "Rule 1", "^(app1)$")
- rule1.Actions.OpenFloating = boolPtr(true)
- rule2 := newTestWindowRule("rule2", "Rule 2", "^(app2)$")
- rule2.Actions.OpenFloating = boolPtr(true)
- rule3 := newTestWindowRule("rule3", "Rule 3", "^(app3)$")
- rule3.Actions.OpenFloating = boolPtr(true)
-
- _ = provider.SetRule(rule1)
- _ = provider.SetRule(rule2)
- _ = provider.SetRule(rule3)
-
- if err := provider.ReorderRules([]string{"rule3", "rule1", "rule2"}); err != nil {
- t.Fatalf("ReorderRules failed: %v", err)
- }
-
- rules, _ := provider.LoadDMSRules()
- if len(rules) != 3 {
- t.Fatalf("expected 3 rules, got %d", len(rules))
- }
- expectedOrder := []string{"rule3", "rule1", "rule2"}
- for i, expectedID := range expectedOrder {
- if rules[i].ID != expectedID {
- t.Errorf("rule %d ID = %q, want %q", i, rules[i].ID, expectedID)
- }
- }
-}
-
-func TestHyprlandParseConfigWithSource(t *testing.T) {
- tmpDir := t.TempDir()
-
- mainConfig := `
-windowrulev2 = float, class:^(mainapp)$
-source = ./extra.conf
-`
- extraConfig := `
-windowrulev2 = tile, class:^(extraapp)$
-`
-
- if err := os.WriteFile(filepath.Join(tmpDir, "hyprland.conf"), []byte(mainConfig), 0644); err != nil {
- t.Fatal(err)
- }
- if err := os.WriteFile(filepath.Join(tmpDir, "extra.conf"), []byte(extraConfig), 0644); err != nil {
- t.Fatal(err)
- }
-
- parser := NewHyprlandRulesParser(tmpDir)
- rules, err := parser.Parse()
- if err != nil {
- t.Fatalf("Parse failed: %v", err)
- }
-
- if len(rules) != 2 {
- t.Errorf("expected 2 rules, got %d", len(rules))
- }
-}
-
-func TestBoolToInt(t *testing.T) {
- if boolToInt(true) != 1 {
- t.Error("boolToInt(true) should be 1")
- }
- if boolToInt(false) != 0 {
- t.Error("boolToInt(false) should be 0")
- }
-}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser.go b/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser.go
deleted file mode 100644
index 5bb5ec4..0000000
--- a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser.go
+++ /dev/null
@@ -1,875 +0,0 @@
-package providers
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
-
- "github.com/sblinch/kdl-go"
- "github.com/sblinch/kdl-go/document"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/windowrules"
-)
-
-type NiriWindowRule struct {
- MatchAppID string
- MatchTitle string
- MatchIsFloating *bool
- MatchIsActive *bool
- MatchIsFocused *bool
- MatchIsActiveInColumn *bool
- MatchIsWindowCastTarget *bool
- MatchIsUrgent *bool
- MatchAtStartup *bool
- Opacity *float64
- OpenFloating *bool
- OpenMaximized *bool
- OpenMaximizedToEdges *bool
- OpenFullscreen *bool
- OpenFocused *bool
- OpenOnOutput string
- OpenOnWorkspace string
- DefaultColumnWidth string
- DefaultWindowHeight string
- VariableRefreshRate *bool
- BlockOutFrom string
- DefaultColumnDisplay string
- ScrollFactor *float64
- CornerRadius *int
- ClipToGeometry *bool
- TiledState *bool
- MinWidth *int
- MaxWidth *int
- MinHeight *int
- MaxHeight *int
- BorderColor string
- FocusRingColor string
- FocusRingOff *bool
- BorderOff *bool
- DrawBorderWithBg *bool
- Source string
-}
-
-type NiriRulesParser struct {
- configDir string
- processedFiles map[string]bool
- rules []NiriWindowRule
- currentSource string
- dmsRulesIncluded bool
- dmsRulesExists bool
- includeCount int
- dmsIncludePos int
- rulesAfterDMS int
- dmsProcessed bool
-}
-
-func NewNiriRulesParser(configDir string) *NiriRulesParser {
- return &NiriRulesParser{
- configDir: configDir,
- processedFiles: make(map[string]bool),
- rules: []NiriWindowRule{},
- dmsIncludePos: -1,
- }
-}
-
-func (p *NiriRulesParser) Parse() ([]NiriWindowRule, error) {
- dmsRulesPath := filepath.Join(p.configDir, "dms", "windowrules.kdl")
- if _, err := os.Stat(dmsRulesPath); err == nil {
- p.dmsRulesExists = true
- }
-
- configPath := filepath.Join(p.configDir, "config.kdl")
- if err := p.parseFile(configPath); err != nil {
- return nil, err
- }
-
- if p.dmsRulesExists && !p.dmsProcessed {
- p.parseDMSRulesDirectly(dmsRulesPath)
- }
-
- return p.rules, nil
-}
-
-func (p *NiriRulesParser) parseDMSRulesDirectly(dmsRulesPath string) {
- data, err := os.ReadFile(dmsRulesPath)
- if err != nil {
- return
- }
-
- doc, err := kdl.Parse(strings.NewReader(string(data)))
- if err != nil {
- return
- }
-
- prevSource := p.currentSource
- p.currentSource = dmsRulesPath
- p.processNodes(doc.Nodes, filepath.Dir(dmsRulesPath))
- p.currentSource = prevSource
- p.dmsProcessed = true
-}
-
-func (p *NiriRulesParser) parseFile(filePath string) error {
- absPath, err := filepath.Abs(filePath)
- if err != nil {
- return err
- }
-
- if p.processedFiles[absPath] {
- return nil
- }
- p.processedFiles[absPath] = true
-
- data, err := os.ReadFile(absPath)
- if err != nil {
- return nil
- }
-
- doc, err := kdl.Parse(strings.NewReader(string(data)))
- if err != nil {
- return err
- }
-
- prevSource := p.currentSource
- p.currentSource = absPath
- baseDir := filepath.Dir(absPath)
- p.processNodes(doc.Nodes, baseDir)
- p.currentSource = prevSource
-
- return nil
-}
-
-func (p *NiriRulesParser) processNodes(nodes []*document.Node, baseDir string) {
- for _, node := range nodes {
- name := node.Name.String()
-
- switch name {
- case "include":
- p.handleInclude(node, baseDir)
- case "window-rule":
- p.parseWindowRuleNode(node)
- }
- }
-}
-
-func (p *NiriRulesParser) handleInclude(node *document.Node, baseDir string) {
- if len(node.Arguments) == 0 {
- return
- }
-
- includePath := strings.Trim(node.Arguments[0].String(), "\"")
- isDMSInclude := includePath == "dms/windowrules.kdl" || strings.HasSuffix(includePath, "/dms/windowrules.kdl")
-
- p.includeCount++
- if isDMSInclude {
- p.dmsRulesIncluded = true
- p.dmsIncludePos = p.includeCount
- p.dmsProcessed = true
- }
-
- fullPath := filepath.Join(baseDir, includePath)
- if filepath.IsAbs(includePath) {
- fullPath = includePath
- }
-
- _ = p.parseFile(fullPath)
-}
-
-func (p *NiriRulesParser) parseWindowRuleNode(node *document.Node) {
- if node.Children == nil {
- return
- }
-
- rule := NiriWindowRule{
- Source: p.currentSource,
- }
-
- for _, child := range node.Children {
- childName := child.Name.String()
-
- switch childName {
- case "match":
- p.parseMatchNode(child, &rule)
- case "opacity":
- if len(child.Arguments) > 0 {
- val := child.Arguments[0].ResolvedValue()
- if f, ok := val.(float64); ok {
- rule.Opacity = &f
- }
- }
- case "open-floating":
- b := p.parseBoolArg(child)
- rule.OpenFloating = &b
- case "open-maximized":
- b := p.parseBoolArg(child)
- rule.OpenMaximized = &b
- case "open-maximized-to-edges":
- b := p.parseBoolArg(child)
- rule.OpenMaximizedToEdges = &b
- case "open-fullscreen":
- b := p.parseBoolArg(child)
- rule.OpenFullscreen = &b
- case "open-focused":
- b := p.parseBoolArg(child)
- rule.OpenFocused = &b
- case "open-on-output":
- if len(child.Arguments) > 0 {
- rule.OpenOnOutput = child.Arguments[0].ValueString()
- }
- case "open-on-workspace":
- if len(child.Arguments) > 0 {
- rule.OpenOnWorkspace = child.Arguments[0].ValueString()
- }
- case "default-column-width":
- rule.DefaultColumnWidth = p.parseSizeNode(child)
- case "default-window-height":
- rule.DefaultWindowHeight = p.parseSizeNode(child)
- case "variable-refresh-rate":
- b := p.parseBoolArg(child)
- rule.VariableRefreshRate = &b
- case "block-out-from":
- if len(child.Arguments) > 0 {
- rule.BlockOutFrom = child.Arguments[0].ValueString()
- }
- case "default-column-display":
- if len(child.Arguments) > 0 {
- rule.DefaultColumnDisplay = child.Arguments[0].ValueString()
- }
- case "scroll-factor":
- if len(child.Arguments) > 0 {
- val := child.Arguments[0].ResolvedValue()
- if f, ok := val.(float64); ok {
- rule.ScrollFactor = &f
- }
- }
- case "geometry-corner-radius":
- if len(child.Arguments) > 0 {
- val := child.Arguments[0].ResolvedValue()
- if i, ok := val.(int64); ok {
- intVal := int(i)
- rule.CornerRadius = &intVal
- }
- }
- case "clip-to-geometry":
- b := p.parseBoolArg(child)
- rule.ClipToGeometry = &b
- case "tiled-state":
- b := p.parseBoolArg(child)
- rule.TiledState = &b
- case "min-width":
- if len(child.Arguments) > 0 {
- val := child.Arguments[0].ResolvedValue()
- if i, ok := val.(int64); ok {
- intVal := int(i)
- rule.MinWidth = &intVal
- }
- }
- case "max-width":
- if len(child.Arguments) > 0 {
- val := child.Arguments[0].ResolvedValue()
- if i, ok := val.(int64); ok {
- intVal := int(i)
- rule.MaxWidth = &intVal
- }
- }
- case "min-height":
- if len(child.Arguments) > 0 {
- val := child.Arguments[0].ResolvedValue()
- if i, ok := val.(int64); ok {
- intVal := int(i)
- rule.MinHeight = &intVal
- }
- }
- case "max-height":
- if len(child.Arguments) > 0 {
- val := child.Arguments[0].ResolvedValue()
- if i, ok := val.(int64); ok {
- intVal := int(i)
- rule.MaxHeight = &intVal
- }
- }
- case "border":
- p.parseBorderNode(child, &rule)
- case "focus-ring":
- p.parseFocusRingNode(child, &rule)
- case "draw-border-with-background":
- b := p.parseBoolArg(child)
- rule.DrawBorderWithBg = &b
- }
- }
-
- p.rules = append(p.rules, rule)
-}
-
-func (p *NiriRulesParser) parseSizeNode(node *document.Node) string {
- if node.Children == nil {
- return ""
- }
- for _, child := range node.Children {
- name := child.Name.String()
- if len(child.Arguments) > 0 {
- val := child.Arguments[0].ResolvedValue()
- switch name {
- case "fixed":
- if i, ok := val.(int64); ok {
- return "fixed " + strconv.FormatInt(i, 10)
- }
- case "proportion":
- if f, ok := val.(float64); ok {
- return "proportion " + strconv.FormatFloat(f, 'f', -1, 64)
- }
- }
- }
- }
- return ""
-}
-
-func (p *NiriRulesParser) parseMatchNode(node *document.Node, rule *NiriWindowRule) {
- if node.Properties == nil {
- return
- }
-
- if val, ok := node.Properties.Get("app-id"); ok {
- rule.MatchAppID = val.ValueString()
- }
- if val, ok := node.Properties.Get("title"); ok {
- rule.MatchTitle = val.ValueString()
- }
- if val, ok := node.Properties.Get("is-floating"); ok {
- b := val.ValueString() == "true"
- rule.MatchIsFloating = &b
- }
- if val, ok := node.Properties.Get("is-active"); ok {
- b := val.ValueString() == "true"
- rule.MatchIsActive = &b
- }
- if val, ok := node.Properties.Get("is-focused"); ok {
- b := val.ValueString() == "true"
- rule.MatchIsFocused = &b
- }
- if val, ok := node.Properties.Get("is-active-in-column"); ok {
- b := val.ValueString() == "true"
- rule.MatchIsActiveInColumn = &b
- }
- if val, ok := node.Properties.Get("is-window-cast-target"); ok {
- b := val.ValueString() == "true"
- rule.MatchIsWindowCastTarget = &b
- }
- if val, ok := node.Properties.Get("is-urgent"); ok {
- b := val.ValueString() == "true"
- rule.MatchIsUrgent = &b
- }
- if val, ok := node.Properties.Get("at-startup"); ok {
- b := val.ValueString() == "true"
- rule.MatchAtStartup = &b
- }
-}
-
-func (p *NiriRulesParser) parseBorderNode(node *document.Node, rule *NiriWindowRule) {
- if node.Children == nil {
- return
- }
-
- for _, child := range node.Children {
- switch child.Name.String() {
- case "off":
- b := true
- rule.BorderOff = &b
- case "active-color":
- if len(child.Arguments) > 0 {
- rule.BorderColor = child.Arguments[0].ValueString()
- }
- }
- }
-}
-
-func (p *NiriRulesParser) parseFocusRingNode(node *document.Node, rule *NiriWindowRule) {
- if node.Children == nil {
- return
- }
-
- for _, child := range node.Children {
- switch child.Name.String() {
- case "off":
- b := true
- rule.FocusRingOff = &b
- case "active-color":
- if len(child.Arguments) > 0 {
- rule.FocusRingColor = child.Arguments[0].ValueString()
- }
- }
- }
-}
-
-func (p *NiriRulesParser) parseBoolArg(node *document.Node) bool {
- if len(node.Arguments) == 0 {
- return true
- }
- return node.Arguments[0].ValueString() != "false"
-}
-
-func (p *NiriRulesParser) HasDMSRulesIncluded() bool {
- return p.dmsRulesIncluded
-}
-
-func (p *NiriRulesParser) buildDMSStatus() *windowrules.DMSRulesStatus {
- status := &windowrules.DMSRulesStatus{
- Exists: p.dmsRulesExists,
- Included: p.dmsRulesIncluded,
- IncludePosition: p.dmsIncludePos,
- TotalIncludes: p.includeCount,
- RulesAfterDMS: p.rulesAfterDMS,
- }
-
- switch {
- case !p.dmsRulesExists:
- status.Effective = false
- status.StatusMessage = "dms/windowrules.kdl does not exist"
- case !p.dmsRulesIncluded:
- status.Effective = false
- status.StatusMessage = "dms/windowrules.kdl is not included in config.kdl"
- case p.rulesAfterDMS > 0:
- status.Effective = true
- status.OverriddenBy = p.rulesAfterDMS
- status.StatusMessage = "Some DMS rules may be overridden by config rules"
- default:
- status.Effective = true
- status.StatusMessage = "DMS window rules are active"
- }
-
- return status
-}
-
-type NiriRulesParseResult struct {
- Rules []NiriWindowRule
- DMSRulesIncluded bool
- DMSStatus *windowrules.DMSRulesStatus
-}
-
-func ParseNiriWindowRules(configDir string) (*NiriRulesParseResult, error) {
- parser := NewNiriRulesParser(configDir)
- rules, err := parser.Parse()
- if err != nil {
- return nil, err
- }
- return &NiriRulesParseResult{
- Rules: rules,
- DMSRulesIncluded: parser.HasDMSRulesIncluded(),
- DMSStatus: parser.buildDMSStatus(),
- }, nil
-}
-
-func ConvertNiriRulesToWindowRules(niriRules []NiriWindowRule) []windowrules.WindowRule {
- result := make([]windowrules.WindowRule, 0, len(niriRules))
- for i, nr := range niriRules {
- wr := windowrules.WindowRule{
- ID: fmt.Sprintf("rule_%d", i),
- Enabled: true,
- Source: nr.Source,
- MatchCriteria: windowrules.MatchCriteria{
- AppID: nr.MatchAppID,
- Title: nr.MatchTitle,
- IsFloating: nr.MatchIsFloating,
- IsActive: nr.MatchIsActive,
- IsFocused: nr.MatchIsFocused,
- IsActiveInColumn: nr.MatchIsActiveInColumn,
- IsWindowCastTarget: nr.MatchIsWindowCastTarget,
- IsUrgent: nr.MatchIsUrgent,
- AtStartup: nr.MatchAtStartup,
- },
- Actions: windowrules.Actions{
- Opacity: nr.Opacity,
- OpenFloating: nr.OpenFloating,
- OpenMaximized: nr.OpenMaximized,
- OpenMaximizedToEdges: nr.OpenMaximizedToEdges,
- OpenFullscreen: nr.OpenFullscreen,
- OpenFocused: nr.OpenFocused,
- OpenOnOutput: nr.OpenOnOutput,
- OpenOnWorkspace: nr.OpenOnWorkspace,
- DefaultColumnWidth: nr.DefaultColumnWidth,
- DefaultWindowHeight: nr.DefaultWindowHeight,
- VariableRefreshRate: nr.VariableRefreshRate,
- BlockOutFrom: nr.BlockOutFrom,
- DefaultColumnDisplay: nr.DefaultColumnDisplay,
- ScrollFactor: nr.ScrollFactor,
- CornerRadius: nr.CornerRadius,
- ClipToGeometry: nr.ClipToGeometry,
- TiledState: nr.TiledState,
- MinWidth: nr.MinWidth,
- MaxWidth: nr.MaxWidth,
- MinHeight: nr.MinHeight,
- MaxHeight: nr.MaxHeight,
- BorderColor: nr.BorderColor,
- FocusRingColor: nr.FocusRingColor,
- FocusRingOff: nr.FocusRingOff,
- BorderOff: nr.BorderOff,
- DrawBorderWithBg: nr.DrawBorderWithBg,
- },
- }
- result = append(result, wr)
- }
- return result
-}
-
-type NiriWritableProvider struct {
- configDir string
-}
-
-func NewNiriWritableProvider(configDir string) *NiriWritableProvider {
- return &NiriWritableProvider{configDir: configDir}
-}
-
-func (p *NiriWritableProvider) Name() string {
- return "niri"
-}
-
-func (p *NiriWritableProvider) GetOverridePath() string {
- return filepath.Join(p.configDir, "dms", "windowrules.kdl")
-}
-
-func (p *NiriWritableProvider) GetRuleSet() (*windowrules.RuleSet, error) {
- result, err := ParseNiriWindowRules(p.configDir)
- if err != nil {
- return nil, err
- }
- return &windowrules.RuleSet{
- Title: "Niri Window Rules",
- Provider: "niri",
- Rules: ConvertNiriRulesToWindowRules(result.Rules),
- DMSRulesIncluded: result.DMSRulesIncluded,
- DMSStatus: result.DMSStatus,
- }, nil
-}
-
-func (p *NiriWritableProvider) SetRule(rule windowrules.WindowRule) error {
- rules, err := p.LoadDMSRules()
- if err != nil {
- rules = []windowrules.WindowRule{}
- }
-
- found := false
- for i, r := range rules {
- if r.ID == rule.ID {
- rules[i] = rule
- found = true
- break
- }
- }
- if !found {
- rules = append(rules, rule)
- }
-
- return p.writeDMSRules(rules)
-}
-
-func (p *NiriWritableProvider) RemoveRule(id string) error {
- rules, err := p.LoadDMSRules()
- if err != nil {
- return err
- }
-
- newRules := make([]windowrules.WindowRule, 0, len(rules))
- for _, r := range rules {
- if r.ID != id {
- newRules = append(newRules, r)
- }
- }
-
- return p.writeDMSRules(newRules)
-}
-
-func (p *NiriWritableProvider) ReorderRules(ids []string) error {
- rules, err := p.LoadDMSRules()
- if err != nil {
- return err
- }
-
- ruleMap := make(map[string]windowrules.WindowRule)
- for _, r := range rules {
- ruleMap[r.ID] = r
- }
-
- newRules := make([]windowrules.WindowRule, 0, len(ids))
- for _, id := range ids {
- if r, ok := ruleMap[id]; ok {
- newRules = append(newRules, r)
- delete(ruleMap, id)
- }
- }
-
- for _, r := range ruleMap {
- newRules = append(newRules, r)
- }
-
- return p.writeDMSRules(newRules)
-}
-
-var niriMetaCommentRegex = regexp.MustCompile(`^//\s*@id=(\S*)\s*@name=(.*)$`)
-
-func (p *NiriWritableProvider) LoadDMSRules() ([]windowrules.WindowRule, error) {
- rulesPath := p.GetOverridePath()
- data, err := os.ReadFile(rulesPath)
- if err != nil {
- if os.IsNotExist(err) {
- return []windowrules.WindowRule{}, nil
- }
- return nil, err
- }
-
- content := string(data)
- lines := strings.Split(content, "\n")
-
- type ruleMeta struct {
- id string
- name string
- }
- var metas []ruleMeta
- var currentID, currentName string
-
- for _, line := range lines {
- trimmed := strings.TrimSpace(line)
- if matches := niriMetaCommentRegex.FindStringSubmatch(trimmed); matches != nil {
- currentID = matches[1]
- currentName = strings.TrimSpace(matches[2])
- continue
- }
- if strings.HasPrefix(trimmed, "window-rule") {
- metas = append(metas, ruleMeta{id: currentID, name: currentName})
- currentID = ""
- currentName = ""
- }
- }
-
- doc, err := kdl.Parse(strings.NewReader(content))
- if err != nil {
- return nil, err
- }
-
- parser := NewNiriRulesParser(p.configDir)
- parser.currentSource = rulesPath
-
- for _, node := range doc.Nodes {
- if node.Name.String() == "window-rule" {
- parser.parseWindowRuleNode(node)
- }
- }
-
- var rules []windowrules.WindowRule
- for i, nr := range parser.rules {
- id := ""
- name := ""
- if i < len(metas) {
- id = metas[i].id
- name = metas[i].name
- }
- if id == "" {
- id = fmt.Sprintf("dms_rule_%d", i)
- }
-
- wr := windowrules.WindowRule{
- ID: id,
- Name: name,
- Enabled: true,
- Source: rulesPath,
- MatchCriteria: windowrules.MatchCriteria{
- AppID: nr.MatchAppID,
- Title: nr.MatchTitle,
- IsFloating: nr.MatchIsFloating,
- IsActive: nr.MatchIsActive,
- IsFocused: nr.MatchIsFocused,
- IsActiveInColumn: nr.MatchIsActiveInColumn,
- IsWindowCastTarget: nr.MatchIsWindowCastTarget,
- IsUrgent: nr.MatchIsUrgent,
- AtStartup: nr.MatchAtStartup,
- },
- Actions: windowrules.Actions{
- Opacity: nr.Opacity,
- OpenFloating: nr.OpenFloating,
- OpenMaximized: nr.OpenMaximized,
- OpenMaximizedToEdges: nr.OpenMaximizedToEdges,
- OpenFullscreen: nr.OpenFullscreen,
- OpenFocused: nr.OpenFocused,
- OpenOnOutput: nr.OpenOnOutput,
- OpenOnWorkspace: nr.OpenOnWorkspace,
- DefaultColumnWidth: nr.DefaultColumnWidth,
- DefaultWindowHeight: nr.DefaultWindowHeight,
- VariableRefreshRate: nr.VariableRefreshRate,
- BlockOutFrom: nr.BlockOutFrom,
- DefaultColumnDisplay: nr.DefaultColumnDisplay,
- ScrollFactor: nr.ScrollFactor,
- CornerRadius: nr.CornerRadius,
- ClipToGeometry: nr.ClipToGeometry,
- TiledState: nr.TiledState,
- MinWidth: nr.MinWidth,
- MaxWidth: nr.MaxWidth,
- MinHeight: nr.MinHeight,
- MaxHeight: nr.MaxHeight,
- BorderColor: nr.BorderColor,
- FocusRingColor: nr.FocusRingColor,
- FocusRingOff: nr.FocusRingOff,
- BorderOff: nr.BorderOff,
- DrawBorderWithBg: nr.DrawBorderWithBg,
- },
- }
-
- rules = append(rules, wr)
- }
-
- return rules, nil
-}
-
-func (p *NiriWritableProvider) writeDMSRules(rules []windowrules.WindowRule) error {
- rulesPath := p.GetOverridePath()
-
- if err := os.MkdirAll(filepath.Dir(rulesPath), 0755); err != nil {
- return err
- }
-
- var lines []string
- lines = append(lines, "// DMS Window Rules - Managed by DankMaterialShell")
- lines = append(lines, "// Do not edit manually - changes may be overwritten")
- lines = append(lines, "")
-
- for _, rule := range rules {
- lines = append(lines, p.formatRule(rule))
- lines = append(lines, "")
- }
-
- return os.WriteFile(rulesPath, []byte(strings.Join(lines, "\n")), 0644)
-}
-
-func (p *NiriWritableProvider) formatRule(rule windowrules.WindowRule) string {
- var lines []string
- lines = append(lines, fmt.Sprintf("// @id=%s @name=%s", rule.ID, rule.Name))
- lines = append(lines, "window-rule {")
-
- m := rule.MatchCriteria
- if m.AppID != "" || m.Title != "" || m.IsFloating != nil || m.IsActive != nil ||
- m.IsFocused != nil || m.IsActiveInColumn != nil || m.IsWindowCastTarget != nil ||
- m.IsUrgent != nil || m.AtStartup != nil {
- var matchProps []string
- if m.AppID != "" {
- matchProps = append(matchProps, fmt.Sprintf("app-id=%q", m.AppID))
- }
- if m.Title != "" {
- matchProps = append(matchProps, fmt.Sprintf("title=%q", m.Title))
- }
- if m.IsFloating != nil {
- matchProps = append(matchProps, fmt.Sprintf("is-floating=%t", *m.IsFloating))
- }
- if m.IsActive != nil {
- matchProps = append(matchProps, fmt.Sprintf("is-active=%t", *m.IsActive))
- }
- if m.IsFocused != nil {
- matchProps = append(matchProps, fmt.Sprintf("is-focused=%t", *m.IsFocused))
- }
- if m.IsActiveInColumn != nil {
- matchProps = append(matchProps, fmt.Sprintf("is-active-in-column=%t", *m.IsActiveInColumn))
- }
- if m.IsWindowCastTarget != nil {
- matchProps = append(matchProps, fmt.Sprintf("is-window-cast-target=%t", *m.IsWindowCastTarget))
- }
- if m.IsUrgent != nil {
- matchProps = append(matchProps, fmt.Sprintf("is-urgent=%t", *m.IsUrgent))
- }
- if m.AtStartup != nil {
- matchProps = append(matchProps, fmt.Sprintf("at-startup=%t", *m.AtStartup))
- }
- lines = append(lines, " match "+strings.Join(matchProps, " "))
- }
-
- a := rule.Actions
- if a.Opacity != nil {
- lines = append(lines, fmt.Sprintf(" opacity %.2f", *a.Opacity))
- }
- if a.OpenFloating != nil && *a.OpenFloating {
- lines = append(lines, " open-floating true")
- }
- if a.OpenMaximized != nil && *a.OpenMaximized {
- lines = append(lines, " open-maximized true")
- }
- if a.OpenMaximizedToEdges != nil && *a.OpenMaximizedToEdges {
- lines = append(lines, " open-maximized-to-edges true")
- }
- if a.OpenFullscreen != nil && *a.OpenFullscreen {
- lines = append(lines, " open-fullscreen true")
- }
- if a.OpenFocused != nil {
- lines = append(lines, fmt.Sprintf(" open-focused %t", *a.OpenFocused))
- }
- if a.OpenOnOutput != "" {
- lines = append(lines, fmt.Sprintf(" open-on-output %q", a.OpenOnOutput))
- }
- if a.OpenOnWorkspace != "" {
- lines = append(lines, fmt.Sprintf(" open-on-workspace %q", a.OpenOnWorkspace))
- }
- if a.DefaultColumnWidth != "" {
- lines = append(lines, formatSizeProperty("default-column-width", a.DefaultColumnWidth))
- }
- if a.DefaultWindowHeight != "" {
- lines = append(lines, formatSizeProperty("default-window-height", a.DefaultWindowHeight))
- }
- if a.VariableRefreshRate != nil && *a.VariableRefreshRate {
- lines = append(lines, " variable-refresh-rate true")
- }
- if a.BlockOutFrom != "" {
- lines = append(lines, fmt.Sprintf(" block-out-from %q", a.BlockOutFrom))
- }
- if a.DefaultColumnDisplay != "" {
- lines = append(lines, fmt.Sprintf(" default-column-display %q", a.DefaultColumnDisplay))
- }
- if a.ScrollFactor != nil {
- lines = append(lines, fmt.Sprintf(" scroll-factor %.2f", *a.ScrollFactor))
- }
- if a.CornerRadius != nil {
- lines = append(lines, fmt.Sprintf(" geometry-corner-radius %d", *a.CornerRadius))
- }
- if a.ClipToGeometry != nil && *a.ClipToGeometry {
- lines = append(lines, " clip-to-geometry true")
- }
- if a.TiledState != nil && *a.TiledState {
- lines = append(lines, " tiled-state true")
- }
- if a.MinWidth != nil {
- lines = append(lines, fmt.Sprintf(" min-width %d", *a.MinWidth))
- }
- if a.MaxWidth != nil {
- lines = append(lines, fmt.Sprintf(" max-width %d", *a.MaxWidth))
- }
- if a.MinHeight != nil {
- lines = append(lines, fmt.Sprintf(" min-height %d", *a.MinHeight))
- }
- if a.MaxHeight != nil {
- lines = append(lines, fmt.Sprintf(" max-height %d", *a.MaxHeight))
- }
- if a.BorderOff != nil && *a.BorderOff {
- lines = append(lines, " border { off; }")
- } else if a.BorderColor != "" {
- lines = append(lines, fmt.Sprintf(" border { active-color %q; }", a.BorderColor))
- }
- if a.FocusRingOff != nil && *a.FocusRingOff {
- lines = append(lines, " focus-ring { off; }")
- } else if a.FocusRingColor != "" {
- lines = append(lines, fmt.Sprintf(" focus-ring { active-color %q; }", a.FocusRingColor))
- }
- if a.DrawBorderWithBg != nil {
- lines = append(lines, fmt.Sprintf(" draw-border-with-background %t", *a.DrawBorderWithBg))
- }
-
- lines = append(lines, "}")
- return strings.Join(lines, "\n")
-}
-
-func formatSizeProperty(name, value string) string {
- parts := strings.SplitN(value, " ", 2)
- if len(parts) == 2 {
- return fmt.Sprintf(" %s { %s %s; }", name, parts[0], parts[1])
- }
- // Bare number without type prefix — default to "fixed"
- if _, err := strconv.Atoi(value); err == nil {
- return fmt.Sprintf(" %s { fixed %s; }", name, value)
- }
- return fmt.Sprintf(" %s { }", name)
-}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser_test.go b/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser_test.go
deleted file mode 100644
index f764b6a..0000000
--- a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser_test.go
+++ /dev/null
@@ -1,335 +0,0 @@
-package providers
-
-import (
- "os"
- "path/filepath"
- "testing"
-)
-
-func TestNiriParseBasicWindowRule(t *testing.T) {
- tmpDir := t.TempDir()
-
- config := `
-window-rule {
- match app-id="^firefox$"
- opacity 0.9
- open-floating true
-}
-`
- if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(config), 0644); err != nil {
- t.Fatal(err)
- }
-
- parser := NewNiriRulesParser(tmpDir)
- rules, err := parser.Parse()
- if err != nil {
- t.Fatalf("Parse failed: %v", err)
- }
-
- if len(rules) != 1 {
- t.Fatalf("expected 1 rule, got %d", len(rules))
- }
-
- rule := rules[0]
- if rule.MatchAppID != "^firefox$" {
- t.Errorf("MatchAppID = %q, want ^firefox$", rule.MatchAppID)
- }
- if rule.Opacity == nil || *rule.Opacity != 0.9 {
- t.Errorf("Opacity = %v, want 0.9", rule.Opacity)
- }
- if rule.OpenFloating == nil || !*rule.OpenFloating {
- t.Error("OpenFloating should be true")
- }
-}
-
-func TestNiriParseMultipleRules(t *testing.T) {
- tmpDir := t.TempDir()
-
- config := `
-window-rule {
- match app-id="app1"
- open-maximized true
-}
-
-window-rule {
- match app-id="app2"
- open-fullscreen true
-}
-`
- if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(config), 0644); err != nil {
- t.Fatal(err)
- }
-
- parser := NewNiriRulesParser(tmpDir)
- rules, err := parser.Parse()
- if err != nil {
- t.Fatalf("Parse failed: %v", err)
- }
-
- if len(rules) != 2 {
- t.Fatalf("expected 2 rules, got %d", len(rules))
- }
-
- if rules[0].MatchAppID != "app1" {
- t.Errorf("rule 0 MatchAppID = %q, want app1", rules[0].MatchAppID)
- }
- if rules[1].MatchAppID != "app2" {
- t.Errorf("rule 1 MatchAppID = %q, want app2", rules[1].MatchAppID)
- }
-}
-
-func TestConvertNiriRulesToWindowRules(t *testing.T) {
- niriRules := []NiriWindowRule{
- {MatchAppID: "^firefox$", Opacity: floatPtr(0.8)},
- {MatchAppID: "^code$", OpenFloating: boolPtr(true)},
- }
-
- result := ConvertNiriRulesToWindowRules(niriRules)
-
- if len(result) != 2 {
- t.Errorf("expected 2 rules, got %d", len(result))
- }
-
- if result[0].MatchCriteria.AppID != "^firefox$" {
- t.Errorf("rule 0 AppID = %q, want ^firefox$", result[0].MatchCriteria.AppID)
- }
- if result[0].Actions.Opacity == nil || *result[0].Actions.Opacity != 0.8 {
- t.Errorf("rule 0 Opacity = %v, want 0.8", result[0].Actions.Opacity)
- }
-
- if result[1].Actions.OpenFloating == nil || !*result[1].Actions.OpenFloating {
- t.Error("rule 1 should have OpenFloating = true")
- }
-}
-
-func TestNiriWritableProvider(t *testing.T) {
- tmpDir := t.TempDir()
- provider := NewNiriWritableProvider(tmpDir)
-
- if provider.Name() != "niri" {
- t.Errorf("Name() = %q, want niri", provider.Name())
- }
-
- expectedPath := filepath.Join(tmpDir, "dms", "windowrules.kdl")
- if provider.GetOverridePath() != expectedPath {
- t.Errorf("GetOverridePath() = %q, want %q", provider.GetOverridePath(), expectedPath)
- }
-}
-
-func TestNiriSetAndLoadDMSRules(t *testing.T) {
- tmpDir := t.TempDir()
- provider := NewNiriWritableProvider(tmpDir)
-
- rule := newTestWindowRule("test_id", "Test Rule", "^firefox$")
- rule.Actions.OpenFloating = boolPtr(true)
- rule.Actions.Opacity = floatPtr(0.85)
-
- if err := provider.SetRule(rule); err != nil {
- t.Fatalf("SetRule failed: %v", err)
- }
-
- rules, err := provider.LoadDMSRules()
- if err != nil {
- t.Fatalf("LoadDMSRules failed: %v", err)
- }
-
- if len(rules) != 1 {
- t.Fatalf("expected 1 rule, got %d", len(rules))
- }
-
- if rules[0].ID != "test_id" {
- t.Errorf("ID = %q, want test_id", rules[0].ID)
- }
- if rules[0].MatchCriteria.AppID != "^firefox$" {
- t.Errorf("AppID = %q, want ^firefox$", rules[0].MatchCriteria.AppID)
- }
-}
-
-func TestNiriRemoveRule(t *testing.T) {
- tmpDir := t.TempDir()
- provider := NewNiriWritableProvider(tmpDir)
-
- rule1 := newTestWindowRule("rule1", "Rule 1", "app1")
- rule1.Actions.OpenFloating = boolPtr(true)
- rule2 := newTestWindowRule("rule2", "Rule 2", "app2")
- rule2.Actions.OpenFloating = boolPtr(true)
-
- _ = provider.SetRule(rule1)
- _ = provider.SetRule(rule2)
-
- if err := provider.RemoveRule("rule1"); err != nil {
- t.Fatalf("RemoveRule failed: %v", err)
- }
-
- rules, _ := provider.LoadDMSRules()
- if len(rules) != 1 {
- t.Fatalf("expected 1 rule after removal, got %d", len(rules))
- }
- if rules[0].ID != "rule2" {
- t.Errorf("remaining rule ID = %q, want rule2", rules[0].ID)
- }
-}
-
-func TestNiriReorderRules(t *testing.T) {
- tmpDir := t.TempDir()
- provider := NewNiriWritableProvider(tmpDir)
-
- rule1 := newTestWindowRule("rule1", "Rule 1", "app1")
- rule1.Actions.OpenFloating = boolPtr(true)
- rule2 := newTestWindowRule("rule2", "Rule 2", "app2")
- rule2.Actions.OpenFloating = boolPtr(true)
- rule3 := newTestWindowRule("rule3", "Rule 3", "app3")
- rule3.Actions.OpenFloating = boolPtr(true)
-
- _ = provider.SetRule(rule1)
- _ = provider.SetRule(rule2)
- _ = provider.SetRule(rule3)
-
- if err := provider.ReorderRules([]string{"rule3", "rule1", "rule2"}); err != nil {
- t.Fatalf("ReorderRules failed: %v", err)
- }
-
- rules, _ := provider.LoadDMSRules()
- if len(rules) != 3 {
- t.Fatalf("expected 3 rules, got %d", len(rules))
- }
- expectedOrder := []string{"rule3", "rule1", "rule2"}
- for i, expectedID := range expectedOrder {
- if rules[i].ID != expectedID {
- t.Errorf("rule %d ID = %q, want %q", i, rules[i].ID, expectedID)
- }
- }
-}
-
-func TestNiriParseConfigWithInclude(t *testing.T) {
- tmpDir := t.TempDir()
-
- mainConfig := `
-window-rule {
- match app-id="mainapp"
- opacity 1.0
-}
-
-include "extra.kdl"
-`
- extraConfig := `
-window-rule {
- match app-id="extraapp"
- open-maximized true
-}
-`
-
- if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(mainConfig), 0644); err != nil {
- t.Fatal(err)
- }
- if err := os.WriteFile(filepath.Join(tmpDir, "extra.kdl"), []byte(extraConfig), 0644); err != nil {
- t.Fatal(err)
- }
-
- parser := NewNiriRulesParser(tmpDir)
- rules, err := parser.Parse()
- if err != nil {
- t.Fatalf("Parse failed: %v", err)
- }
-
- if len(rules) != 2 {
- t.Errorf("expected 2 rules, got %d", len(rules))
- }
-}
-
-func TestNiriParseSizeNode(t *testing.T) {
- tmpDir := t.TempDir()
-
- config := `
-window-rule {
- match app-id="testapp"
- default-column-width { fixed 800; }
- default-window-height { proportion 0.5; }
-}
-`
- if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(config), 0644); err != nil {
- t.Fatal(err)
- }
-
- parser := NewNiriRulesParser(tmpDir)
- rules, err := parser.Parse()
- if err != nil {
- t.Fatalf("Parse failed: %v", err)
- }
-
- if len(rules) != 1 {
- t.Fatalf("expected 1 rule, got %d", len(rules))
- }
-
- if rules[0].DefaultColumnWidth != "fixed 800" {
- t.Errorf("DefaultColumnWidth = %q, want 'fixed 800'", rules[0].DefaultColumnWidth)
- }
- if rules[0].DefaultWindowHeight != "proportion 0.5" {
- t.Errorf("DefaultWindowHeight = %q, want 'proportion 0.5'", rules[0].DefaultWindowHeight)
- }
-}
-
-func TestFormatSizeProperty(t *testing.T) {
- tests := []struct {
- name string
- propName string
- value string
- want string
- }{
- {
- name: "fixed size",
- propName: "default-column-width",
- value: "fixed 800",
- want: " default-column-width { fixed 800; }",
- },
- {
- name: "proportion",
- propName: "default-window-height",
- value: "proportion 0.5",
- want: " default-window-height { proportion 0.5; }",
- },
- {
- name: "invalid format",
- propName: "default-column-width",
- value: "invalid",
- want: " default-column-width { }",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := formatSizeProperty(tt.propName, tt.value)
- if result != tt.want {
- t.Errorf("formatSizeProperty(%q, %q) = %q, want %q",
- tt.propName, tt.value, result, tt.want)
- }
- })
- }
-}
-
-func TestNiriDMSRulesStatus(t *testing.T) {
- tmpDir := t.TempDir()
-
- config := `
-window-rule {
- match app-id="testapp"
- opacity 0.9
-}
-`
- if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(config), 0644); err != nil {
- t.Fatal(err)
- }
-
- result, err := ParseNiriWindowRules(tmpDir)
- if err != nil {
- t.Fatalf("ParseNiriWindowRules failed: %v", err)
- }
-
- if result.DMSStatus == nil {
- t.Fatal("DMSStatus should not be nil")
- }
-
- if result.DMSStatus.Exists {
- t.Error("DMSStatus.Exists should be false when dms rules file doesn't exist")
- }
-}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/providers_test.go b/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/providers_test.go
deleted file mode 100644
index de84a38..0000000
--- a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/providers/providers_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package providers
-
-import "github.com/AvengeMedia/DankMaterialShell/core/internal/windowrules"
-
-func newTestWindowRule(id, name, appID string) windowrules.WindowRule {
- return windowrules.WindowRule{
- ID: id,
- Name: name,
- Enabled: true,
- MatchCriteria: windowrules.MatchCriteria{
- AppID: appID,
- },
- }
-}
-
-func boolPtr(b bool) *bool {
- return &b
-}
-
-func floatPtr(f float64) *float64 {
- return &f
-}
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/types.go b/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/types.go
deleted file mode 100644
index 556acb7..0000000
--- a/raveos-theme/hyprland/theme-data/DankMaterialShell/core/internal/windowrules/types.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package windowrules
-
-type MatchCriteria struct {
- AppID string `json:"appId,omitempty"`
- Title string `json:"title,omitempty"`
- IsFloating *bool `json:"isFloating,omitempty"`
- IsActive *bool `json:"isActive,omitempty"`
- IsFocused *bool `json:"isFocused,omitempty"`
- IsActiveInColumn *bool `json:"isActiveInColumn,omitempty"`
- IsWindowCastTarget *bool `json:"isWindowCastTarget,omitempty"`
- IsUrgent *bool `json:"isUrgent,omitempty"`
- AtStartup *bool `json:"atStartup,omitempty"`
- XWayland *bool `json:"xwayland,omitempty"`
- Fullscreen *bool `json:"fullscreen,omitempty"`
- Pinned *bool `json:"pinned,omitempty"`
- Initialised *bool `json:"initialised,omitempty"`
-}
-
-type Actions struct {
- Opacity *float64 `json:"opacity,omitempty"`
- OpenFloating *bool `json:"openFloating,omitempty"`
- OpenMaximized *bool `json:"openMaximized,omitempty"`
- OpenMaximizedToEdges *bool `json:"openMaximizedToEdges,omitempty"`
- OpenFullscreen *bool `json:"openFullscreen,omitempty"`
- OpenFocused *bool `json:"openFocused,omitempty"`
- OpenOnOutput string `json:"openOnOutput,omitempty"`
- OpenOnWorkspace string `json:"openOnWorkspace,omitempty"`
- DefaultColumnWidth string `json:"defaultColumnWidth,omitempty"`
- DefaultWindowHeight string `json:"defaultWindowHeight,omitempty"`
- VariableRefreshRate *bool `json:"variableRefreshRate,omitempty"`
- BlockOutFrom string `json:"blockOutFrom,omitempty"`
- DefaultColumnDisplay string `json:"defaultColumnDisplay,omitempty"`
- ScrollFactor *float64 `json:"scrollFactor,omitempty"`
- CornerRadius *int `json:"cornerRadius,omitempty"`
- ClipToGeometry *bool `json:"clipToGeometry,omitempty"`
- TiledState *bool `json:"tiledState,omitempty"`
- MinWidth *int `json:"minWidth,omitempty"`
- MaxWidth *int `json:"maxWidth,omitempty"`
- MinHeight *int `json:"minHeight,omitempty"`
- MaxHeight *int `json:"maxHeight,omitempty"`
- BorderColor string `json:"borderColor,omitempty"`
- FocusRingColor string `json:"focusRingColor,omitempty"`
- FocusRingOff *bool `json:"focusRingOff,omitempty"`
- BorderOff *bool `json:"borderOff,omitempty"`
- DrawBorderWithBg *bool `json:"drawBorderWithBackground,omitempty"`
- Size string `json:"size,omitempty"`
- Move string `json:"move,omitempty"`
- Monitor string `json:"monitor,omitempty"`
- Workspace string `json:"workspace,omitempty"`
- Tile *bool `json:"tile,omitempty"`
- NoFocus *bool `json:"nofocus,omitempty"`
- NoBorder *bool `json:"noborder,omitempty"`
- NoShadow *bool `json:"noshadow,omitempty"`
- NoDim *bool `json:"nodim,omitempty"`
- NoBlur *bool `json:"noblur,omitempty"`
- NoAnim *bool `json:"noanim,omitempty"`
- NoRounding *bool `json:"norounding,omitempty"`
- Pin *bool `json:"pin,omitempty"`
- Opaque *bool `json:"opaque,omitempty"`
- ForcergbX *bool `json:"forcergbx,omitempty"`
- Idleinhibit string `json:"idleinhibit,omitempty"`
-}
-
-type WindowRule struct {
- ID string `json:"id"`
- Name string `json:"name,omitempty"`
- Enabled bool `json:"enabled"`
- MatchCriteria MatchCriteria `json:"matchCriteria"`
- Actions Actions `json:"actions"`
- Source string `json:"source,omitempty"`
-}
-
-type DMSRulesStatus struct {
- Exists bool `json:"exists"`
- Included bool `json:"included"`
- IncludePosition int `json:"includePosition"`
- TotalIncludes int `json:"totalIncludes"`
- RulesAfterDMS int `json:"rulesAfterDms"`
- Effective bool `json:"effective"`
- OverriddenBy int `json:"overriddenBy"`
- StatusMessage string `json:"statusMessage"`
-}
-
-type RuleSet struct {
- Title string `json:"title"`
- Provider string `json:"provider"`
- Rules []WindowRule `json:"rules"`
- DMSRulesIncluded bool `json:"dmsRulesIncluded"`
- DMSStatus *DMSRulesStatus `json:"dmsStatus,omitempty"`
-}
-
-type Provider interface {
- Name() string
- GetRuleSet() (*RuleSet, error)
-}
-
-type WritableProvider interface {
- Provider
- SetRule(rule WindowRule) error
- RemoveRule(id string) error
- ReorderRules(ids []string) error
- GetOverridePath() string
-}