summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext
diff options
context:
space:
mode:
authorAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
committerAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
commit70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch)
treeab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext
parent5d94c0a7d44a2255b81815a52a7056a94a39842d (diff)
downloadRaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.tar.gz
RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.zip
Replaced file structures for themes in the correct format
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context.go200
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context_test.go128
2 files changed, 328 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context.go
new file mode 100644
index 0000000..9bbb30a
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context.go
@@ -0,0 +1,200 @@
+package wlcontext
+
+import (
+ "fmt"
+ "golang.org/x/sys/unix"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
+ wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
+)
+
+type WaylandContext interface {
+ Display() *wlclient.Display
+ Post(fn func())
+ FatalError() <-chan error
+ Start()
+ Close()
+}
+
+var _ WaylandContext = (*SharedContext)(nil)
+
+type SharedContext struct {
+ display *wlclient.Display
+ stopChan chan struct{}
+ fatalError chan error
+ cmdQueue chan func()
+ wakeR int
+ wakeW int
+ wg sync.WaitGroup
+ mu sync.Mutex
+ started bool
+}
+
+func New() (*SharedContext, error) {
+ display, err := wlclient.Connect("")
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errdefs.ErrNoWaylandDisplay, err)
+ }
+
+ fds := make([]int, 2)
+ if err := unix.Pipe(fds); err != nil {
+ display.Context().Close()
+ return nil, fmt.Errorf("failed to create wake pipe: %w", err)
+ }
+ if err := unix.SetNonblock(fds[0], true); err != nil {
+ unix.Close(fds[0])
+ unix.Close(fds[1])
+ display.Context().Close()
+ return nil, fmt.Errorf("failed to set wake pipe nonblock: %w", err)
+ }
+ if err := unix.SetNonblock(fds[1], true); err != nil {
+ unix.Close(fds[0])
+ unix.Close(fds[1])
+ display.Context().Close()
+ return nil, fmt.Errorf("failed to set wake pipe nonblock: %w", err)
+ }
+
+ sc := &SharedContext{
+ display: display,
+ stopChan: make(chan struct{}),
+ fatalError: make(chan error, 1),
+ cmdQueue: make(chan func(), 256),
+ wakeR: fds[0],
+ wakeW: fds[1],
+ started: false,
+ }
+
+ return sc, nil
+}
+
+func (sc *SharedContext) Start() {
+ sc.mu.Lock()
+ defer sc.mu.Unlock()
+
+ if sc.started {
+ return
+ }
+
+ sc.started = true
+ sc.wg.Add(1)
+ go sc.eventDispatcher()
+}
+
+func (sc *SharedContext) Display() *wlclient.Display {
+ return sc.display
+}
+
+func (sc *SharedContext) Post(fn func()) {
+ select {
+ case sc.cmdQueue <- fn:
+ if _, err := unix.Write(sc.wakeW, []byte{1}); err != nil && err != unix.EAGAIN {
+ log.Errorf("wake pipe write error: %v", err)
+ }
+ default:
+ }
+}
+
+func (sc *SharedContext) FatalError() <-chan error {
+ return sc.fatalError
+}
+
+func (sc *SharedContext) eventDispatcher() {
+ defer sc.wg.Done()
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Errorf("FATAL: Wayland event dispatcher panic: %v", r)
+ log.Error(err)
+ select {
+ case sc.fatalError <- err:
+ default:
+ }
+ }
+ }()
+
+ ctx := sc.display.Context()
+ wlFd := ctx.Fd()
+
+ pollFds := []unix.PollFd{
+ {Fd: int32(wlFd), Events: unix.POLLIN},
+ {Fd: int32(sc.wakeR), Events: unix.POLLIN},
+ }
+
+ consecutiveErrors := 0
+ const maxConsecutiveErrors = 20
+
+ for {
+ sc.drainCmdQueue()
+
+ select {
+ case <-sc.stopChan:
+ return
+ default:
+ }
+
+ _, err := unix.Poll(pollFds, -1)
+ switch {
+ case err == unix.EINTR:
+ continue
+ case err != nil:
+ log.Errorf("Poll error: %v", err)
+ return
+ }
+
+ if pollFds[1].Revents&unix.POLLIN != 0 {
+ var buf [64]byte
+ if _, err := unix.Read(sc.wakeR, buf[:]); err != nil && err != unix.EAGAIN {
+ log.Errorf("wake pipe read error: %v", err)
+ }
+ }
+
+ if pollFds[0].Revents&unix.POLLIN == 0 {
+ continue
+ }
+
+ if err := ctx.Dispatch(); err != nil && !os.IsTimeout(err) {
+ consecutiveErrors++
+ log.Warnf("Wayland connection error (%d/%d): %v", consecutiveErrors, maxConsecutiveErrors, err)
+
+ if consecutiveErrors >= maxConsecutiveErrors {
+ log.Errorf("Fatal: Wayland connection unrecoverable after %d attempts. Exiting dispatcher.", maxConsecutiveErrors)
+ return
+ }
+
+ time.Sleep(100 * time.Millisecond * time.Duration(consecutiveErrors))
+ continue
+ }
+
+ consecutiveErrors = 0
+ }
+}
+
+func (sc *SharedContext) drainCmdQueue() {
+ for {
+ select {
+ case fn := <-sc.cmdQueue:
+ fn()
+ default:
+ return
+ }
+ }
+}
+
+func (sc *SharedContext) Close() {
+ close(sc.stopChan)
+ if _, err := unix.Write(sc.wakeW, []byte{1}); err != nil && err != unix.EAGAIN {
+ log.Errorf("wake pipe write error on close: %v", err)
+ }
+ sc.wg.Wait()
+
+ unix.Close(sc.wakeR)
+ unix.Close(sc.wakeW)
+
+ if sc.display == nil {
+ return
+ }
+ sc.display.Context().Close()
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context_test.go
new file mode 100644
index 0000000..11ecfbf
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context_test.go
@@ -0,0 +1,128 @@
+package wlcontext
+
+import (
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "golang.org/x/sys/unix"
+)
+
+func newTestSharedContext(t *testing.T, queueSize int) *SharedContext {
+ t.Helper()
+ fds := make([]int, 2)
+ if err := unix.Pipe(fds); err != nil {
+ t.Fatalf("failed to create test pipe: %v", err)
+ }
+ t.Cleanup(func() {
+ unix.Close(fds[0])
+ unix.Close(fds[1])
+ })
+ return &SharedContext{
+ cmdQueue: make(chan func(), queueSize),
+ stopChan: make(chan struct{}),
+ wakeR: fds[0],
+ wakeW: fds[1],
+ }
+}
+
+func TestSharedContext_ConcurrentPostNonBlocking(t *testing.T) {
+ sc := newTestSharedContext(t, 256)
+
+ var wg sync.WaitGroup
+ const goroutines = 100
+ const iterations = 50
+
+ for i := 0; i < goroutines; i++ {
+ wg.Add(1)
+ go func(id int) {
+ defer wg.Done()
+ for j := 0; j < iterations; j++ {
+ sc.Post(func() {
+ _ = id + j
+ })
+ }
+ }(i)
+ }
+
+ wg.Wait()
+}
+
+func TestSharedContext_PostQueueFull(t *testing.T) {
+ sc := newTestSharedContext(t, 2)
+
+ sc.Post(func() {})
+ sc.Post(func() {})
+ sc.Post(func() {})
+ sc.Post(func() {})
+
+ assert.Len(t, sc.cmdQueue, 2)
+}
+
+func TestSharedContext_StartMultipleTimes(t *testing.T) {
+ sc := newTestSharedContext(t, 256)
+ sc.started = true
+
+ var wg sync.WaitGroup
+ const goroutines = 10
+
+ for i := 0; i < goroutines; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ sc.Start()
+ }()
+ }
+
+ wg.Wait()
+
+ assert.True(t, sc.started)
+}
+
+func TestSharedContext_DrainCmdQueue(t *testing.T) {
+ sc := newTestSharedContext(t, 256)
+
+ counter := 0
+ for i := 0; i < 10; i++ {
+ sc.cmdQueue <- func() {
+ counter++
+ }
+ }
+
+ sc.drainCmdQueue()
+
+ assert.Equal(t, 10, counter)
+ assert.Len(t, sc.cmdQueue, 0)
+}
+
+func TestSharedContext_DrainCmdQueueEmpty(t *testing.T) {
+ sc := newTestSharedContext(t, 256)
+
+ sc.drainCmdQueue()
+
+ assert.Len(t, sc.cmdQueue, 0)
+}
+
+func TestSharedContext_ConcurrentDrainAndPost(t *testing.T) {
+ sc := newTestSharedContext(t, 256)
+
+ var wg sync.WaitGroup
+
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 100; i++ {
+ sc.Post(func() {})
+ }
+ }()
+
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 50; i++ {
+ sc.drainCmdQueue()
+ }
+ }()
+
+ wg.Wait()
+}