diff options
| author | Nippy <nippy@rp1.hu> | 2026-05-09 13:33:09 +0200 |
|---|---|---|
| committer | Nippy <nippy@rp1.hu> | 2026-05-09 13:33:09 +0200 |
| commit | a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch) | |
| tree | 1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl | |
| parent | 34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff) | |
| download | RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip | |
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl')
4 files changed, 0 insertions, 1202 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/handlers.go deleted file mode 100644 index e83cc84..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/handlers.go +++ /dev/null @@ -1,138 +0,0 @@ -package dwl - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "dwl manager not initialized") - return - } - - switch req.Method { - case "dwl.getState": - handleGetState(conn, req, manager) - case "dwl.setTags": - handleSetTags(conn, req, manager) - case "dwl.setClientTags": - handleSetClientTags(conn, req, manager) - case "dwl.setLayout": - handleSetLayout(conn, req, manager) - case "dwl.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleSetTags(conn net.Conn, req models.Request, manager *Manager) { - output, ok := models.Get[string](req, "output") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - tagmask, ok := models.Get[float64](req, "tagmask") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'tagmask' parameter") - return - } - - toggleTagset, ok := models.Get[float64](req, "toggleTagset") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'toggleTagset' parameter") - return - } - - if err := manager.SetTags(output, uint32(tagmask), uint32(toggleTagset)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "tags set"}) -} - -func handleSetClientTags(conn net.Conn, req models.Request, manager *Manager) { - output, ok := models.Get[string](req, "output") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - andTags, ok := models.Get[float64](req, "andTags") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'andTags' parameter") - return - } - - xorTags, ok := models.Get[float64](req, "xorTags") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'xorTags' parameter") - return - } - - if err := manager.SetClientTags(output, uint32(andTags), uint32(xorTags)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "client tags set"}) -} - -func handleSetLayout(conn net.Conn, req models.Request, manager *Manager) { - output, ok := models.Get[string](req, "output") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - index, ok := models.Get[float64](req, "index") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'index' parameter") - return - } - - if err := manager.SetLayout(output, uint32(index)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "layout set"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager.go deleted file mode 100644 index 0d21afa..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager.go +++ /dev/null @@ -1,522 +0,0 @@ -package dwl - -import ( - "fmt" - "time" - - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/dwl_ipc" -) - -func NewManager(display wlclient.WaylandDisplay) (*Manager, error) { - m := &Manager{ - display: display, - ctx: display.Context(), - cmdq: make(chan cmd, 128), - outputSetupReq: make(chan uint32, 16), - stopChan: make(chan struct{}), - - dirty: make(chan struct{}, 1), - layouts: make([]string, 0), - } - - if err := m.setupRegistry(); err != nil { - return nil, err - } - - m.updateState() - - m.notifierWg.Add(1) - go m.notifier() - - m.wg.Add(1) - go m.waylandActor() - - return m, nil -} - -func (m *Manager) post(fn func()) { - select { - case m.cmdq <- cmd{fn: fn}: - default: - log.Warn("DWL actor command queue full, dropping command") - } -} - -func (m *Manager) waylandActor() { - defer m.wg.Done() - - for { - select { - case <-m.stopChan: - return - case c := <-m.cmdq: - c.fn() - case outputID := <-m.outputSetupReq: - out, exists := m.outputs.Load(outputID) - if !exists { - log.Warnf("DWL: Output %d no longer exists, skipping setup", outputID) - continue - } - - if out.ipcOutput != nil { - continue - } - - mgr, ok := m.manager.(*dwl_ipc.ZdwlIpcManagerV2) - if !ok || mgr == nil { - log.Errorf("DWL: Manager not available for output %d setup", outputID) - continue - } - - log.Infof("DWL: Setting up ipcOutput for dynamically added output %d", outputID) - if err := m.setupOutput(mgr, out.output); err != nil { - log.Errorf("DWL: Failed to setup output %d: %v", outputID, err) - } else { - m.updateState() - } - } - } -} - -func (m *Manager) setupRegistry() error { - log.Info("DWL: starting registry setup") - - registry, err := m.display.GetRegistry() - if err != nil { - return fmt.Errorf("failed to get registry: %w", err) - } - m.registry = registry - - outputs := make([]*wlclient.Output, 0) - outputRegNames := make(map[uint32]uint32) - var dwlMgr *dwl_ipc.ZdwlIpcManagerV2 - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case dwl_ipc.ZdwlIpcManagerV2InterfaceName: - log.Infof("DWL: found %s", dwl_ipc.ZdwlIpcManagerV2InterfaceName) - manager := dwl_ipc.NewZdwlIpcManagerV2(m.ctx) - version := e.Version - if version > 2 { - version = 2 - } - if err := registry.Bind(e.Name, e.Interface, version, manager); err == nil { - dwlMgr = manager - log.Info("DWL: manager bound successfully") - - // Set handlers immediately after binding, before roundtrips - manager.SetTagsHandler(func(e dwl_ipc.ZdwlIpcManagerV2TagsEvent) { - log.Infof("DWL: Tags count: %d", e.Amount) - m.tagCount = e.Amount - m.updateState() - }) - - manager.SetLayoutHandler(func(e dwl_ipc.ZdwlIpcManagerV2LayoutEvent) { - log.Infof("DWL: Layout: %s", e.Name) - m.layouts = append(m.layouts, e.Name) - m.updateState() - }) - } else { - log.Errorf("DWL: failed to bind manager: %v", err) - } - case "wl_output": - log.Debugf("DWL: found wl_output (name=%d)", e.Name) - output := wlclient.NewOutput(m.ctx) - - outState := &outputState{ - registryName: e.Name, - output: output, - tags: make([]TagState, 0), - } - - output.SetNameHandler(func(ev wlclient.OutputNameEvent) { - log.Debugf("DWL: Output name: %s (registry=%d)", ev.Name, e.Name) - outState.name = ev.Name - }) - - output.SetDescriptionHandler(func(ev wlclient.OutputDescriptionEvent) { - log.Debugf("DWL: Output description: %s", ev.Description) - }) - - version := e.Version - if version > 4 { - version = 4 - } - if err := registry.Bind(e.Name, e.Interface, version, output); err == nil { - outputID := output.ID() - outState.id = outputID - log.Infof("DWL: Bound wl_output id=%d registry_name=%d", outputID, e.Name) - outputs = append(outputs, output) - outputRegNames[outputID] = e.Name - - m.outputs.Store(outputID, outState) - - if m.manager != nil { - select { - case m.outputSetupReq <- outputID: - log.Debugf("DWL: Queued setup for output %d", outputID) - default: - log.Warnf("DWL: Setup queue full, output %d will not be initialized", outputID) - } - } - } else { - log.Errorf("DWL: Failed to bind wl_output: %v", err) - } - } - }) - - registry.SetGlobalRemoveHandler(func(e wlclient.RegistryGlobalRemoveEvent) { - m.post(func() { - var outToRelease *outputState - m.outputs.Range(func(id uint32, out *outputState) bool { - if out.registryName == e.Name { - log.Infof("DWL: Output %d removed", id) - outToRelease = out - m.outputs.Delete(id) - return false - } - return true - }) - - if outToRelease != nil { - if ipcOut, ok := outToRelease.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2); ok && ipcOut != nil { - m.wlMutex.Lock() - ipcOut.Release() - m.wlMutex.Unlock() - log.Debugf("DWL: Released ipcOutput for removed output %d", outToRelease.id) - } - m.updateState() - } - }) - }) - - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("first roundtrip failed: %w", err) - } - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("second roundtrip failed: %w", err) - } - - if dwlMgr == nil { - log.Info("DWL: manager not found in registry") - return fmt.Errorf("dwl_ipc_manager_v2 not available") - } - - m.manager = dwlMgr - - for _, output := range outputs { - if err := m.setupOutput(dwlMgr, output); err != nil { - log.Warnf("DWL: Failed to setup output %d: %v", output.ID(), err) - } - } - - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("final roundtrip failed: %w", err) - } - - log.Info("DWL: registry setup complete") - return nil -} - -func (m *Manager) setupOutput(manager *dwl_ipc.ZdwlIpcManagerV2, output *wlclient.Output) error { - m.wlMutex.Lock() - ipcOutput, err := manager.GetOutput(output) - m.wlMutex.Unlock() - if err != nil { - return fmt.Errorf("failed to get dwl output: %w", err) - } - - outState, exists := m.outputs.Load(output.ID()) - if !exists { - return fmt.Errorf("output state not found for id %d", output.ID()) - } - outState.ipcOutput = ipcOutput - - ipcOutput.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) { - outState.active = e.Active - }) - - ipcOutput.SetTagHandler(func(e dwl_ipc.ZdwlIpcOutputV2TagEvent) { - updated := false - for i, tag := range outState.tags { - if tag.Tag == e.Tag { - outState.tags[i] = TagState{ - Tag: e.Tag, - State: e.State, - Clients: e.Clients, - Focused: e.Focused, - } - updated = true - break - } - } - - if !updated { - outState.tags = append(outState.tags, TagState{ - Tag: e.Tag, - State: e.State, - Clients: e.Clients, - Focused: e.Focused, - }) - } - - m.updateState() - }) - - ipcOutput.SetLayoutHandler(func(e dwl_ipc.ZdwlIpcOutputV2LayoutEvent) { - outState.layout = e.Layout - }) - - ipcOutput.SetTitleHandler(func(e dwl_ipc.ZdwlIpcOutputV2TitleEvent) { - outState.title = e.Title - }) - - ipcOutput.SetAppidHandler(func(e dwl_ipc.ZdwlIpcOutputV2AppidEvent) { - outState.appID = e.Appid - }) - - ipcOutput.SetLayoutSymbolHandler(func(e dwl_ipc.ZdwlIpcOutputV2LayoutSymbolEvent) { - outState.layoutSymbol = e.Layout - }) - - ipcOutput.SetKbLayoutHandler(func(e dwl_ipc.ZdwlIpcOutputV2KbLayoutEvent) { - outState.kbLayout = e.KbLayout - }) - - ipcOutput.SetKeymodeHandler(func(e dwl_ipc.ZdwlIpcOutputV2KeymodeEvent) { - outState.keymode = e.Keymode - }) - - ipcOutput.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) { - m.updateState() - }) - - return nil -} - -func (m *Manager) updateState() { - outputs := make(map[string]*OutputState) - activeOutput := "" - - m.outputs.Range(func(key uint32, out *outputState) bool { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - - tagsCopy := make([]TagState, len(out.tags)) - copy(tagsCopy, out.tags) - - outputs[name] = &OutputState{ - Name: name, - Active: out.active, - Tags: tagsCopy, - Layout: out.layout, - LayoutSymbol: out.layoutSymbol, - Title: out.title, - AppID: out.appID, - KbLayout: out.kbLayout, - Keymode: out.keymode, - } - - if out.active != 0 { - activeOutput = name - } - return true - }) - - newState := State{ - Outputs: outputs, - TagCount: m.tagCount, - Layouts: m.layouts, - ActiveOutput: activeOutput, - } - - m.stateMutex.Lock() - m.state = &newState - m.stateMutex.Unlock() - - m.notifySubscribers() -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - - currentState := m.GetState() - - if m.lastNotified != nil && !stateChanged(m.lastNotified, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- currentState: - default: - log.Warn("DWL: subscriber channel full, dropping update") - } - return true - }) - - stateCopy := currentState - m.lastNotified = &stateCopy - pending = false - } - } -} - -func (m *Manager) ensureOutputSetup(out *outputState) error { - if out.ipcOutput != nil { - return nil - } - - return fmt.Errorf("output not yet initialized - setup in progress, retry in a moment") -} - -func (m *Manager) SetTags(outputName string, tagmask uint32, toggleTagset uint32) error { - availableOutputs := make([]string, 0) - var targetOut *outputState - m.outputs.Range(func(key uint32, out *outputState) bool { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - availableOutputs = append(availableOutputs, name) - if name == outputName { - targetOut = out - return false - } - return true - }) - - if targetOut == nil { - return fmt.Errorf("output not found: %s (available: %v)", outputName, availableOutputs) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetTags(tagmask, toggleTagset) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) SetClientTags(outputName string, andTags uint32, xorTags uint32) error { - var targetOut *outputState - m.outputs.Range(func(key uint32, out *outputState) bool { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - if name == outputName { - targetOut = out - return false - } - return true - }) - - if targetOut == nil { - return fmt.Errorf("output not found: %s", outputName) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetClientTags(andTags, xorTags) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) SetLayout(outputName string, index uint32) error { - var targetOut *outputState - m.outputs.Range(func(key uint32, out *outputState) bool { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - if name == outputName { - targetOut = out - return false - } - return true - }) - - if targetOut == nil { - return fmt.Errorf("output not found: %s", outputName) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetLayout(index) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) Close() { - close(m.stopChan) - m.wg.Wait() - m.notifierWg.Wait() - - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - m.outputs.Range(func(key uint32, out *outputState) bool { - if ipcOut, ok := out.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2); ok { - ipcOut.Release() - } - m.outputs.Delete(key) - return true - }) - - if mgr, ok := m.manager.(*dwl_ipc.ZdwlIpcManagerV2); ok { - mgr.Release() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager_test.go deleted file mode 100644 index 13af137..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager_test.go +++ /dev/null @@ -1,366 +0,0 @@ -package dwl - -import ( - "errors" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - mocks_wlclient "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/wlclient" -) - -func TestStateChanged_BothNil(t *testing.T) { - assert.True(t, stateChanged(nil, nil)) -} - -func TestStateChanged_OneNil(t *testing.T) { - s := &State{TagCount: 9} - assert.True(t, stateChanged(s, nil)) - assert.True(t, stateChanged(nil, s)) -} - -func TestStateChanged_TagCountDiffers(t *testing.T) { - a := &State{TagCount: 9, Outputs: make(map[string]*OutputState), Layouts: []string{}} - b := &State{TagCount: 10, Outputs: make(map[string]*OutputState), Layouts: []string{}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_LayoutLengthDiffers(t *testing.T) { - a := &State{TagCount: 9, Layouts: []string{"tile"}, Outputs: make(map[string]*OutputState)} - b := &State{TagCount: 9, Layouts: []string{"tile", "monocle"}, Outputs: make(map[string]*OutputState)} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_ActiveOutputDiffers(t *testing.T) { - a := &State{TagCount: 9, ActiveOutput: "eDP-1", Outputs: make(map[string]*OutputState), Layouts: []string{}} - b := &State{TagCount: 9, ActiveOutput: "HDMI-A-1", Outputs: make(map[string]*OutputState), Layouts: []string{}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputCountDiffers(t *testing.T) { - a := &State{ - TagCount: 9, - Outputs: map[string]*OutputState{"eDP-1": {}}, - Layouts: []string{}, - } - b := &State{ - TagCount: 9, - Outputs: map[string]*OutputState{}, - Layouts: []string{}, - } - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputFieldsDiffer(t *testing.T) { - a := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Active: 1, Layout: 0, Title: "Firefox"}, - }, - } - b := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Active: 0, Layout: 0, Title: "Firefox"}, - }, - } - assert.True(t, stateChanged(a, b)) - - b.Outputs["eDP-1"].Active = 1 - b.Outputs["eDP-1"].Layout = 1 - assert.True(t, stateChanged(a, b)) - - b.Outputs["eDP-1"].Layout = 0 - b.Outputs["eDP-1"].Title = "Code" - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_TagsDiffer(t *testing.T) { - a := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Tags: []TagState{{Tag: 1, State: 1, Clients: 2, Focused: 1}}}, - }, - } - b := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Tags: []TagState{{Tag: 1, State: 2, Clients: 2, Focused: 1}}}, - }, - } - assert.True(t, stateChanged(a, b)) - - b.Outputs["eDP-1"].Tags[0].State = 1 - b.Outputs["eDP-1"].Tags[0].Clients = 3 - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_Equal(t *testing.T) { - a := &State{ - TagCount: 9, - ActiveOutput: "eDP-1", - Layouts: []string{"tile", "monocle"}, - Outputs: map[string]*OutputState{ - "eDP-1": { - Name: "eDP-1", - Active: 1, - Layout: 0, - LayoutSymbol: "[]=", - Title: "Firefox", - AppID: "firefox", - KbLayout: "us", - Keymode: "", - Tags: []TagState{{Tag: 1, State: 1, Clients: 2, Focused: 1}}, - }, - }, - } - b := &State{ - TagCount: 9, - ActiveOutput: "eDP-1", - Layouts: []string{"tile", "monocle"}, - Outputs: map[string]*OutputState{ - "eDP-1": { - Name: "eDP-1", - Active: 1, - Layout: 0, - LayoutSymbol: "[]=", - Title: "Firefox", - AppID: "firefox", - KbLayout: "us", - Keymode: "", - Tags: []TagState{{Tag: 1, State: 1, Clients: 2, Focused: 1}}, - }, - }, - } - assert.False(t, stateChanged(a, b)) -} - -func TestManager_ConcurrentGetState(t *testing.T) { - m := &Manager{ - state: &State{ - TagCount: 9, - Layouts: []string{"tile"}, - Outputs: map[string]*OutputState{"eDP-1": {Name: "eDP-1"}}, - }, - } - - var wg sync.WaitGroup - const goroutines = 50 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - s := m.GetState() - _ = s.TagCount - _ = s.Outputs - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.stateMutex.Lock() - m.state = &State{ - TagCount: uint32(j % 10), - Layouts: []string{"tile", "monocle"}, - Outputs: map[string]*OutputState{"eDP-1": {Active: uint32(j % 2)}}, - } - m.stateMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentSubscriberAccess(t *testing.T) { - m := &Manager{ - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - var wg sync.WaitGroup - const goroutines = 20 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - subID := string(rune('a' + id)) - ch := m.Subscribe(subID) - assert.NotNil(t, ch) - time.Sleep(time.Millisecond) - m.Unsubscribe(subID) - }(i) - } - - wg.Wait() -} - -func TestManager_SyncmapOutputsConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - state := &outputState{ - id: key, - name: "test-output", - active: uint32(j % 2), - tags: []TagState{{Tag: uint32(j), State: 1}}, - } - m.outputs.Store(key, state) - - if loaded, ok := m.outputs.Load(key); ok { - assert.Equal(t, key, loaded.id) - } - - m.outputs.Range(func(k uint32, v *outputState) bool { - _ = v.name - _ = v.active - return true - }) - } - - m.outputs.Delete(key) - }(i) - } - - wg.Wait() -} - -func TestManager_NotifySubscribersNonBlocking(t *testing.T) { - m := &Manager{ - dirty: make(chan struct{}, 1), - } - - for i := 0; i < 10; i++ { - m.notifySubscribers() - } - - assert.Len(t, m.dirty, 1) -} - -func TestManager_PostQueueFull(t *testing.T) { - m := &Manager{ - cmdq: make(chan cmd, 2), - stopChan: make(chan struct{}), - } - - m.post(func() {}) - m.post(func() {}) - m.post(func() {}) - m.post(func() {}) - - assert.Len(t, m.cmdq, 2) -} - -func TestManager_GetStateNilState(t *testing.T) { - m := &Manager{} - - s := m.GetState() - assert.NotNil(t, s.Outputs) - assert.NotNil(t, s.Layouts) - assert.Equal(t, uint32(0), s.TagCount) -} - -func TestTagState_Fields(t *testing.T) { - tag := TagState{ - Tag: 1, - State: 2, - Clients: 3, - Focused: 1, - } - - assert.Equal(t, uint32(1), tag.Tag) - assert.Equal(t, uint32(2), tag.State) - assert.Equal(t, uint32(3), tag.Clients) - assert.Equal(t, uint32(1), tag.Focused) -} - -func TestOutputState_Fields(t *testing.T) { - out := OutputState{ - Name: "eDP-1", - Active: 1, - Tags: []TagState{{Tag: 1}}, - Layout: 0, - LayoutSymbol: "[]=", - Title: "Firefox", - AppID: "firefox", - KbLayout: "us", - Keymode: "", - } - - assert.Equal(t, "eDP-1", out.Name) - assert.Equal(t, uint32(1), out.Active) - assert.Len(t, out.Tags, 1) - assert.Equal(t, "[]=", out.LayoutSymbol) -} - -func TestStateChanged_NewOutputAppears(t *testing.T) { - a := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Name: "eDP-1"}, - }, - } - b := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Name: "eDP-1"}, - "HDMI-A-1": {Name: "HDMI-A-1"}, - }, - } - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_TagsLengthDiffers(t *testing.T) { - a := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Tags: []TagState{{Tag: 1}}}, - }, - } - b := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Tags: []TagState{{Tag: 1}, {Tag: 2}}}, - }, - } - assert.True(t, stateChanged(a, b)) -} - -func TestNewManager_GetRegistryError(t *testing.T) { - mockDisplay := mocks_wlclient.NewMockWaylandDisplay(t) - - mockDisplay.EXPECT().Context().Return(nil) - mockDisplay.EXPECT().GetRegistry().Return(nil, errors.New("failed to get registry")) - - _, err := NewManager(mockDisplay) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to get registry") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/types.go deleted file mode 100644 index 2b17537..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/types.go +++ /dev/null @@ -1,176 +0,0 @@ -package dwl - -import ( - "sync" - - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type TagState struct { - Tag uint32 `json:"tag"` - State uint32 `json:"state"` - Clients uint32 `json:"clients"` - Focused uint32 `json:"focused"` -} - -type OutputState struct { - Name string `json:"name"` - Active uint32 `json:"active"` - Tags []TagState `json:"tags"` - Layout uint32 `json:"layout"` - LayoutSymbol string `json:"layoutSymbol"` - Title string `json:"title"` - AppID string `json:"appId"` - KbLayout string `json:"kbLayout"` - Keymode string `json:"keymode"` -} - -type State struct { - Outputs map[string]*OutputState `json:"outputs"` - TagCount uint32 `json:"tagCount"` - Layouts []string `json:"layouts"` - ActiveOutput string `json:"activeOutput"` -} - -type cmd struct { - fn func() -} - -type Manager struct { - display wlclient.WaylandDisplay - ctx *wlclient.Context - registry *wlclient.Registry - manager any - - outputs syncmap.Map[uint32, *outputState] - - tagCount uint32 - layouts []string - - wlMutex sync.Mutex - cmdq chan cmd - outputSetupReq chan uint32 - stopChan chan struct{} - wg sync.WaitGroup - - subscribers syncmap.Map[string, chan State] - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotified *State - - stateMutex sync.RWMutex - state *State -} - -type outputState struct { - id uint32 - registryName uint32 - output *wlclient.Output - ipcOutput any - name string - active uint32 - tags []TagState - layout uint32 - layoutSymbol string - title string - appID string - kbLayout string - keymode string -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{ - Outputs: make(map[string]*OutputState), - Layouts: []string{}, - TagCount: 0, - } - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - - m.subscribers.Store(id, ch) - - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func stateChanged(old, new *State) bool { - if old == nil || new == nil { - return true - } - if old.TagCount != new.TagCount { - return true - } - if len(old.Layouts) != len(new.Layouts) { - return true - } - if old.ActiveOutput != new.ActiveOutput { - return true - } - if len(old.Outputs) != len(new.Outputs) { - return true - } - - for name, newOut := range new.Outputs { - oldOut, exists := old.Outputs[name] - if !exists { - return true - } - if oldOut.Active != newOut.Active { - return true - } - if oldOut.Layout != newOut.Layout { - return true - } - if oldOut.LayoutSymbol != newOut.LayoutSymbol { - return true - } - if oldOut.Title != newOut.Title { - return true - } - if oldOut.AppID != newOut.AppID { - return true - } - if oldOut.KbLayout != newOut.KbLayout { - return true - } - if oldOut.Keymode != newOut.Keymode { - return true - } - if len(oldOut.Tags) != len(newOut.Tags) { - return true - } - for i, newTag := range newOut.Tags { - if i >= len(oldOut.Tags) { - return true - } - oldTag := oldOut.Tags[i] - if oldTag.Tag != newTag.Tag || oldTag.State != newTag.State || - oldTag.Clients != newTag.Clients || oldTag.Focused != newTag.Focused { - return true - } - } - } - - return false -} |