detached c41bf8c

fix: finalize player cleanup follow-ups

commit
c41bf8c3e15fab59f0da8d4c84fe73a642fb19fa
author
brice · · 2026-04-10 04:07 UTC
parent
8a75379
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
6 files · +80 -58
M cmd/pipewiregen/internal/emitter/emitter_test.go +31 -14
@@ -157,21 +157,38 @@ func testCleanupModel() *model.Model {
157157 // TestEmitGeneratesCleanupBindings verifies that cleanup bindings are generated.
158158 // This test ensures pw_main_loop_quit and pw_stream_destroy are present in generated files.
159159 func TestEmitGeneratesCleanupBindings(t *testing.T) {
160- // Load the actual pipewire.json from the project root
161- root, err := filepath.Abs("../../../..")
162- if err != nil {
163- t.Fatalf("failed to get project root: %v", err)
164- }
165-
166- actualModel, err := parser.Load(filepath.Join(root, "gen", "pipewire.json"))
167- if err != nil {
168- t.Fatalf("failed to load pipewire.json: %v", err)
169- }
160+ t.Run("unit model", func(t *testing.T) {
161+ paths, err := Emit(testCleanupModel(), t.TempDir())
162+ if err != nil {
163+ t.Fatalf("Emit returned error: %v", err)
164+ }
165+
166+ assertCleanupBindings(t, paths)
167+ })
168+
169+ t.Run("checked in model", func(t *testing.T) {
170+ // Load the actual pipewire.json from the project root
171+ root, err := filepath.Abs("../../../..")
172+ if err != nil {
173+ t.Fatalf("failed to get project root: %v", err)
174+ }
175+
176+ actualModel, err := parser.Load(filepath.Join(root, "gen", "pipewire.json"))
177+ if err != nil {
178+ t.Fatalf("failed to load pipewire.json: %v", err)
179+ }
180+
181+ paths, err := Emit(actualModel, t.TempDir())
182+ if err != nil {
183+ t.Fatalf("Emit returned error: %v", err)
184+ }
185+
186+ assertCleanupBindings(t, paths)
187+ })
188+}
170189
171- paths, err := Emit(actualModel, t.TempDir())
172- if err != nil {
173- t.Fatalf("Emit returned error: %v", err)
174- }
190+func assertCleanupBindings(t *testing.T, paths map[string][]byte) {
191+ t.Helper()
175192
176193 // Verify loop_gen.go contains pw_main_loop_quit
177194 loopCapiContent := string(paths["internal/capi/loop_gen.go"])
@@ -157,21 +157,38 @@ func testCleanupModel() *model.Model {
157 // TestEmitGeneratesCleanupBindings verifies that cleanup bindings are generated.157 // TestEmitGeneratesCleanupBindings verifies that cleanup bindings are generated.
158 // This test ensures pw_main_loop_quit and pw_stream_destroy are present in generated files.158 // This test ensures pw_main_loop_quit and pw_stream_destroy are present in generated files.
159 func TestEmitGeneratesCleanupBindings(t *testing.T) {159 func TestEmitGeneratesCleanupBindings(t *testing.T) {
160- // Load the actual pipewire.json from the project root160+ t.Run("unit model", func(t *testing.T) {
161- root, err := filepath.Abs("../../../..")161+ paths, err := Emit(testCleanupModel(), t.TempDir())
162- if err != nil {162+ if err != nil {
163- t.Fatalf("failed to get project root: %v", err)163+ t.Fatalf("Emit returned error: %v", err)
164- }164+ }
165-165+
166- actualModel, err := parser.Load(filepath.Join(root, "gen", "pipewire.json"))166+ assertCleanupBindings(t, paths)
167- if err != nil {167+ })
168- t.Fatalf("failed to load pipewire.json: %v", err)168+
169- }169+ t.Run("checked in model", func(t *testing.T) {
170+ // Load the actual pipewire.json from the project root
171+ root, err := filepath.Abs("../../../..")
172+ if err != nil {
173+ t.Fatalf("failed to get project root: %v", err)
174+ }
175+
176+ actualModel, err := parser.Load(filepath.Join(root, "gen", "pipewire.json"))
177+ if err != nil {
178+ t.Fatalf("failed to load pipewire.json: %v", err)
179+ }
180+
181+ paths, err := Emit(actualModel, t.TempDir())
182+ if err != nil {
183+ t.Fatalf("Emit returned error: %v", err)
184+ }
185+
186+ assertCleanupBindings(t, paths)
187+ })
188+}
170 189
171- paths, err := Emit(actualModel, t.TempDir())190+func assertCleanupBindings(t *testing.T, paths map[string][]byte) {
172- if err != nil {191+ t.Helper()
173- t.Fatalf("Emit returned error: %v", err)
174- }
175 192
176 // Verify loop_gen.go contains pw_main_loop_quit193 // Verify loop_gen.go contains pw_main_loop_quit
177 loopCapiContent := string(paths["internal/capi/loop_gen.go"])194 loopCapiContent := string(paths["internal/capi/loop_gen.go"])
M internal/capi/stream_ops.go +13 -3
@@ -15,6 +15,14 @@ var (
1515 errMainLoopCreate = errors.New("pw_main_loop_new returned nil")
1616 )
1717
18+const pwVersionStreamEvents = 2
19+
20+const (
21+ pwDirectionOutput = 1
22+ pwIDAny = 0xffffffff
23+ pwStreamFlagAutoConnectMapBuffers = 0x05
24+)
25+
1826 // streamCallbackStorage keeps the pw_stream_events struct and its
1927 // associated Go callback alive for the lifetime of the stream.
2028 // Without this, the stack-allocated values in CreatePlaybackStream
@@ -34,7 +42,7 @@ type streamOpsImpl struct {
3442 // Verify interface compliance at compile time.
3543 var _ portout.StreamOps = (*streamOpsImpl)(nil)
3644
37-func (s *streamOpsImpl) CreatePlaybackStream(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func()) (unsafe.Pointer, error) {
45+func (s *streamOpsImpl) CreatePlaybackStream(loopPtr unsafe.Pointer, name string, onProcess func()) (unsafe.Pointer, error) {
3846 nameBytes := append([]byte(name), 0)
3947
4048 // Build persistent callback storage that outlives this function.
@@ -43,7 +51,7 @@ func (s *streamOpsImpl) CreatePlaybackStream(loopPtr unsafe.Pointer, name string
4351 onProcess()
4452 }
4553 storage.events = pw_stream_events{
46- version: 0, // PW_VERSION_STREAM_EVENTS
54+ version: pwVersionStreamEvents,
4755 process: &storage.processCb,
4856 }
4957
@@ -72,7 +80,7 @@ func (s *streamOpsImpl) CreatePlaybackStream(loopPtr unsafe.Pointer, name string
7280 func (s *streamOpsImpl) ConnectPlaybackStream(streamPtr unsafe.Pointer) error {
7381 // PW_DIRECTION_OUTPUT = 1, PW_ID_ANY = 0xffffffff
7482 // PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS = 0x01 | 0x04
75- ret := pw_stream_connect(streamPtr, 1, 0xffffffff, 0x05, nil, 0)
83+ ret := pw_stream_connect(streamPtr, pwDirectionOutput, pwIDAny, pwStreamFlagAutoConnectMapBuffers, nil, 0)
7684 if ret < 0 {
7785 return errStreamConnect
7886 }
@@ -99,6 +107,8 @@ func (s *streamOpsImpl) QueueBuffer(streamPtr unsafe.Pointer, bufPtr unsafe.Poin
99107 return nil
100108 }
101109
110+// DisconnectStream intentionally ignores the PipeWire return code.
111+// Close-path cleanup is best-effort here, and destroy is the definitive release.
102112 func (s *streamOpsImpl) DisconnectStream(streamPtr unsafe.Pointer) {
103113 pw_stream_disconnect(streamPtr)
104114 }
@@ -15,6 +15,14 @@ var (
15 errMainLoopCreate = errors.New("pw_main_loop_new returned nil")15 errMainLoopCreate = errors.New("pw_main_loop_new returned nil")
16 )16 )
17 17
18+const pwVersionStreamEvents = 2
19+
20+const (
21+ pwDirectionOutput = 1
22+ pwIDAny = 0xffffffff
23+ pwStreamFlagAutoConnectMapBuffers = 0x05
24+)
25+
18 // streamCallbackStorage keeps the pw_stream_events struct and its26 // streamCallbackStorage keeps the pw_stream_events struct and its
19 // associated Go callback alive for the lifetime of the stream.27 // associated Go callback alive for the lifetime of the stream.
20 // Without this, the stack-allocated values in CreatePlaybackStream28 // Without this, the stack-allocated values in CreatePlaybackStream
@@ -34,7 +42,7 @@ type streamOpsImpl struct {
34 // Verify interface compliance at compile time.42 // Verify interface compliance at compile time.
35 var _ portout.StreamOps = (*streamOpsImpl)(nil)43 var _ portout.StreamOps = (*streamOpsImpl)(nil)
36 44
37-func (s *streamOpsImpl) CreatePlaybackStream(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func()) (unsafe.Pointer, error) {45+func (s *streamOpsImpl) CreatePlaybackStream(loopPtr unsafe.Pointer, name string, onProcess func()) (unsafe.Pointer, error) {
38 nameBytes := append([]byte(name), 0)46 nameBytes := append([]byte(name), 0)
39 47
40 // Build persistent callback storage that outlives this function.48 // Build persistent callback storage that outlives this function.
@@ -43,7 +51,7 @@ func (s *streamOpsImpl) CreatePlaybackStream(loopPtr unsafe.Pointer, name string
43 onProcess()51 onProcess()
44 }52 }
45 storage.events = pw_stream_events{53 storage.events = pw_stream_events{
46- version: 0, // PW_VERSION_STREAM_EVENTS54+ version: pwVersionStreamEvents,
47 process: &storage.processCb,55 process: &storage.processCb,
48 }56 }
49 57
@@ -72,7 +80,7 @@ func (s *streamOpsImpl) CreatePlaybackStream(loopPtr unsafe.Pointer, name string
72 func (s *streamOpsImpl) ConnectPlaybackStream(streamPtr unsafe.Pointer) error {80 func (s *streamOpsImpl) ConnectPlaybackStream(streamPtr unsafe.Pointer) error {
73 // PW_DIRECTION_OUTPUT = 1, PW_ID_ANY = 0xffffffff81 // PW_DIRECTION_OUTPUT = 1, PW_ID_ANY = 0xffffffff
74 // PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS = 0x01 | 0x0482 // PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS = 0x01 | 0x04
75- ret := pw_stream_connect(streamPtr, 1, 0xffffffff, 0x05, nil, 0)83+ ret := pw_stream_connect(streamPtr, pwDirectionOutput, pwIDAny, pwStreamFlagAutoConnectMapBuffers, nil, 0)
76 if ret < 0 {84 if ret < 0 {
77 return errStreamConnect85 return errStreamConnect
78 }86 }
@@ -99,6 +107,8 @@ func (s *streamOpsImpl) QueueBuffer(streamPtr unsafe.Pointer, bufPtr unsafe.Poin
99 return nil107 return nil
100 }108 }
101 109
110+// DisconnectStream intentionally ignores the PipeWire return code.
111+// Close-path cleanup is best-effort here, and destroy is the definitive release.
102 func (s *streamOpsImpl) DisconnectStream(streamPtr unsafe.Pointer) {112 func (s *streamOpsImpl) DisconnectStream(streamPtr unsafe.Pointer) {
103 pw_stream_disconnect(streamPtr)113 pw_stream_disconnect(streamPtr)
104 }114 }
M internal/core/player.go +21 -10
@@ -207,30 +207,41 @@ func (p *player) setPaused(paused bool) {
207207 // deactivateStream deactivates the stream via StreamOps.
208208 // Returns an error if deactivation fails. No-op when streamOps is nil.
209209 func (p *player) deactivateStream() error {
210- if p.streamOps == nil || p.streamPtr == nil {
210+ p.mu.Lock()
211+ streamOps := p.streamOps
212+ streamPtr := p.streamPtr
213+ p.mu.Unlock()
214+
215+ if streamOps == nil || streamPtr == nil {
211216 return nil
212217 }
213- return p.streamOps.SetStreamActive(p.streamPtr, false)
218+ return streamOps.SetStreamActive(streamPtr, false)
214219 }
215220
216221 // teardown releases player-owned stream and main-loop resources through
217222 // StreamOps. Safe to call when fields are nil or on repeated invocation.
218223 func (p *player) teardown() {
219- if p.streamOps == nil {
224+ p.mu.Lock()
225+ streamOps := p.streamOps
226+ streamPtr := p.streamPtr
227+ loopPtr := p.loopPtr
228+ p.streamPtr = nil
229+ p.loopPtr = nil
230+ p.mu.Unlock()
231+
232+ if streamOps == nil {
220233 return
221234 }
222235
223236 // Destroy stream first (it depends on the loop).
224- if p.streamPtr != nil {
225- p.streamOps.DestroyStream(p.streamPtr)
226- p.streamPtr = nil
237+ if streamPtr != nil {
238+ streamOps.DestroyStream(streamPtr)
227239 }
228240
229241 // Quit then destroy the main loop.
230- if p.loopPtr != nil {
231- p.streamOps.QuitMainLoop(p.loopPtr)
232- p.streamOps.DestroyMainLoop(p.loopPtr)
233- p.loopPtr = nil
242+ if loopPtr != nil {
243+ streamOps.QuitMainLoop(loopPtr)
244+ streamOps.DestroyMainLoop(loopPtr)
234245 }
235246 }
236247
@@ -207,30 +207,41 @@ func (p *player) setPaused(paused bool) {
207 // deactivateStream deactivates the stream via StreamOps.207 // deactivateStream deactivates the stream via StreamOps.
208 // Returns an error if deactivation fails. No-op when streamOps is nil.208 // Returns an error if deactivation fails. No-op when streamOps is nil.
209 func (p *player) deactivateStream() error {209 func (p *player) deactivateStream() error {
210- if p.streamOps == nil || p.streamPtr == nil {210+ p.mu.Lock()
211+ streamOps := p.streamOps
212+ streamPtr := p.streamPtr
213+ p.mu.Unlock()
214+
215+ if streamOps == nil || streamPtr == nil {
211 return nil216 return nil
212 }217 }
213- return p.streamOps.SetStreamActive(p.streamPtr, false)218+ return streamOps.SetStreamActive(streamPtr, false)
214 }219 }
215 220
216 // teardown releases player-owned stream and main-loop resources through221 // teardown releases player-owned stream and main-loop resources through
217 // StreamOps. Safe to call when fields are nil or on repeated invocation.222 // StreamOps. Safe to call when fields are nil or on repeated invocation.
218 func (p *player) teardown() {223 func (p *player) teardown() {
219- if p.streamOps == nil {224+ p.mu.Lock()
225+ streamOps := p.streamOps
226+ streamPtr := p.streamPtr
227+ loopPtr := p.loopPtr
228+ p.streamPtr = nil
229+ p.loopPtr = nil
230+ p.mu.Unlock()
231+
232+ if streamOps == nil {
220 return233 return
221 }234 }
222 235
223 // Destroy stream first (it depends on the loop).236 // Destroy stream first (it depends on the loop).
224- if p.streamPtr != nil {237+ if streamPtr != nil {
225- p.streamOps.DestroyStream(p.streamPtr)238+ streamOps.DestroyStream(streamPtr)
226- p.streamPtr = nil
227 }239 }
228 240
229 // Quit then destroy the main loop.241 // Quit then destroy the main loop.
230- if p.loopPtr != nil {242+ if loopPtr != nil {
231- p.streamOps.QuitMainLoop(p.loopPtr)243+ streamOps.QuitMainLoop(loopPtr)
232- p.streamOps.DestroyMainLoop(p.loopPtr)244+ streamOps.DestroyMainLoop(loopPtr)
233- p.loopPtr = nil
234 }245 }
235 }246 }
236 247
M internal/core/player_test.go +0 -4
@@ -6,7 +6,6 @@ import (
66 "unsafe"
77
88 "github.com/bnema/purego-pipewire/internal/ports/out/mocks"
9- "github.com/stretchr/testify/mock"
109 )
1110
1211 func TestPlayerStopIsRestartableButCloseIsTerminal(t *testing.T) {
@@ -369,6 +368,3 @@ func TestPlayerTeardownWithOnlyLoopPtr(t *testing.T) {
369368 t.Error("expected loopPtr to be nil after teardown")
370369 }
371370 }
372-
373-// Ensure mock.Mock is referenced to satisfy the import.
374-var _ mock.TestingT = (*testing.T)(nil)
@@ -6,7 +6,6 @@ import (
6 "unsafe"6 "unsafe"
7 7
8 "github.com/bnema/purego-pipewire/internal/ports/out/mocks"8 "github.com/bnema/purego-pipewire/internal/ports/out/mocks"
9- "github.com/stretchr/testify/mock"
10 )9 )
11 10
12 func TestPlayerStopIsRestartableButCloseIsTerminal(t *testing.T) {11 func TestPlayerStopIsRestartableButCloseIsTerminal(t *testing.T) {
@@ -369,6 +368,3 @@ func TestPlayerTeardownWithOnlyLoopPtr(t *testing.T) {
369 t.Error("expected loopPtr to be nil after teardown")368 t.Error("expected loopPtr to be nil after teardown")
370 }369 }
371 }370 }
372-
373-// Ensure mock.Mock is referenced to satisfy the import.
374-var _ mock.TestingT = (*testing.T)(nil)
M internal/ports/out/mocks/mock_StreamOps.go +14 -26
@@ -142,8 +142,8 @@ func (_c *MockStreamOps_CreateMainLoop_Call) RunAndReturn(run func() (unsafe.Poi
142142 }
143143
144144 // CreatePlaybackStream provides a mock function for the type MockStreamOps
145-func (_mock *MockStreamOps) CreatePlaybackStream(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func()) (unsafe.Pointer, error) {
146- ret := _mock.Called(loopPtr, name, sampleRate, channels, onProcess)
145+func (_mock *MockStreamOps) CreatePlaybackStream(loopPtr unsafe.Pointer, name string, onProcess func()) (unsafe.Pointer, error) {
146+ ret := _mock.Called(loopPtr, name, onProcess)
147147
148148 if len(ret) == 0 {
149149 panic("no return value specified for CreatePlaybackStream")
@@ -151,16 +151,16 @@ func (_mock *MockStreamOps) CreatePlaybackStream(loopPtr unsafe.Pointer, name st
151151
152152 var r0 unsafe.Pointer
153153 var r1 error
154- if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, string, int, int, func()) (unsafe.Pointer, error)); ok {
155- return returnFunc(loopPtr, name, sampleRate, channels, onProcess)
154+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, string, func()) (unsafe.Pointer, error)); ok {
155+ return returnFunc(loopPtr, name, onProcess)
156156 }
157- if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, string, int, int, func()) unsafe.Pointer); ok {
158- r0 = returnFunc(loopPtr, name, sampleRate, channels, onProcess)
157+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, string, func()) unsafe.Pointer); ok {
158+ r0 = returnFunc(loopPtr, name, onProcess)
159159 } else {
160160 r0 = ret.Get(0).(unsafe.Pointer)
161161 }
162- if returnFunc, ok := ret.Get(1).(func(unsafe.Pointer, string, int, int, func()) error); ok {
163- r1 = returnFunc(loopPtr, name, sampleRate, channels, onProcess)
162+ if returnFunc, ok := ret.Get(1).(func(unsafe.Pointer, string, func()) error); ok {
163+ r1 = returnFunc(loopPtr, name, onProcess)
164164 } else {
165165 r1 = ret.Error(1)
166166 }
@@ -175,14 +175,12 @@ type MockStreamOps_CreatePlaybackStream_Call struct {
175175 // CreatePlaybackStream is a helper method to define mock.On call
176176 // - loopPtr unsafe.Pointer
177177 // - name string
178-// - sampleRate int
179-// - channels int
180178 // - onProcess func()
181-func (_e *MockStreamOps_Expecter) CreatePlaybackStream(loopPtr interface{}, name interface{}, sampleRate interface{}, channels interface{}, onProcess interface{}) *MockStreamOps_CreatePlaybackStream_Call {
182- return &MockStreamOps_CreatePlaybackStream_Call{Call: _e.mock.On("CreatePlaybackStream", loopPtr, name, sampleRate, channels, onProcess)}
179+func (_e *MockStreamOps_Expecter) CreatePlaybackStream(loopPtr interface{}, name interface{}, onProcess interface{}) *MockStreamOps_CreatePlaybackStream_Call {
180+ return &MockStreamOps_CreatePlaybackStream_Call{Call: _e.mock.On("CreatePlaybackStream", loopPtr, name, onProcess)}
183181 }
184182
185-func (_c *MockStreamOps_CreatePlaybackStream_Call) Run(run func(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func())) *MockStreamOps_CreatePlaybackStream_Call {
183+func (_c *MockStreamOps_CreatePlaybackStream_Call) Run(run func(loopPtr unsafe.Pointer, name string, onProcess func())) *MockStreamOps_CreatePlaybackStream_Call {
186184 _c.Call.Run(func(args mock.Arguments) {
187185 var arg0 unsafe.Pointer
188186 if args[0] != nil {
@@ -192,24 +190,14 @@ func (_c *MockStreamOps_CreatePlaybackStream_Call) Run(run func(loopPtr unsafe.P
192190 if args[1] != nil {
193191 arg1 = args[1].(string)
194192 }
195- var arg2 int
193+ var arg2 func()
196194 if args[2] != nil {
197- arg2 = args[2].(int)
198- }
199- var arg3 int
200- if args[3] != nil {
201- arg3 = args[3].(int)
202- }
203- var arg4 func()
204- if args[4] != nil {
205- arg4 = args[4].(func())
195+ arg2 = args[2].(func())
206196 }
207197 run(
208198 arg0,
209199 arg1,
210200 arg2,
211- arg3,
212- arg4,
213201 )
214202 })
215203 return _c
@@ -220,7 +208,7 @@ func (_c *MockStreamOps_CreatePlaybackStream_Call) Return(streamPtr unsafe.Point
220208 return _c
221209 }
222210
223-func (_c *MockStreamOps_CreatePlaybackStream_Call) RunAndReturn(run func(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func()) (unsafe.Pointer, error)) *MockStreamOps_CreatePlaybackStream_Call {
211+func (_c *MockStreamOps_CreatePlaybackStream_Call) RunAndReturn(run func(loopPtr unsafe.Pointer, name string, onProcess func()) (unsafe.Pointer, error)) *MockStreamOps_CreatePlaybackStream_Call {
224212 _c.Call.Return(run)
225213 return _c
226214 }
@@ -142,8 +142,8 @@ func (_c *MockStreamOps_CreateMainLoop_Call) RunAndReturn(run func() (unsafe.Poi
142 }142 }
143 143
144 // CreatePlaybackStream provides a mock function for the type MockStreamOps144 // CreatePlaybackStream provides a mock function for the type MockStreamOps
145-func (_mock *MockStreamOps) CreatePlaybackStream(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func()) (unsafe.Pointer, error) {145+func (_mock *MockStreamOps) CreatePlaybackStream(loopPtr unsafe.Pointer, name string, onProcess func()) (unsafe.Pointer, error) {
146- ret := _mock.Called(loopPtr, name, sampleRate, channels, onProcess)146+ ret := _mock.Called(loopPtr, name, onProcess)
147 147
148 if len(ret) == 0 {148 if len(ret) == 0 {
149 panic("no return value specified for CreatePlaybackStream")149 panic("no return value specified for CreatePlaybackStream")
@@ -151,16 +151,16 @@ func (_mock *MockStreamOps) CreatePlaybackStream(loopPtr unsafe.Pointer, name st
151 151
152 var r0 unsafe.Pointer152 var r0 unsafe.Pointer
153 var r1 error153 var r1 error
154- if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, string, int, int, func()) (unsafe.Pointer, error)); ok {154+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, string, func()) (unsafe.Pointer, error)); ok {
155- return returnFunc(loopPtr, name, sampleRate, channels, onProcess)155+ return returnFunc(loopPtr, name, onProcess)
156 }156 }
157- if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, string, int, int, func()) unsafe.Pointer); ok {157+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, string, func()) unsafe.Pointer); ok {
158- r0 = returnFunc(loopPtr, name, sampleRate, channels, onProcess)158+ r0 = returnFunc(loopPtr, name, onProcess)
159 } else {159 } else {
160 r0 = ret.Get(0).(unsafe.Pointer)160 r0 = ret.Get(0).(unsafe.Pointer)
161 }161 }
162- if returnFunc, ok := ret.Get(1).(func(unsafe.Pointer, string, int, int, func()) error); ok {162+ if returnFunc, ok := ret.Get(1).(func(unsafe.Pointer, string, func()) error); ok {
163- r1 = returnFunc(loopPtr, name, sampleRate, channels, onProcess)163+ r1 = returnFunc(loopPtr, name, onProcess)
164 } else {164 } else {
165 r1 = ret.Error(1)165 r1 = ret.Error(1)
166 }166 }
@@ -175,14 +175,12 @@ type MockStreamOps_CreatePlaybackStream_Call struct {
175 // CreatePlaybackStream is a helper method to define mock.On call175 // CreatePlaybackStream is a helper method to define mock.On call
176 // - loopPtr unsafe.Pointer176 // - loopPtr unsafe.Pointer
177 // - name string177 // - name string
178-// - sampleRate int
179-// - channels int
180 // - onProcess func()178 // - onProcess func()
181-func (_e *MockStreamOps_Expecter) CreatePlaybackStream(loopPtr interface{}, name interface{}, sampleRate interface{}, channels interface{}, onProcess interface{}) *MockStreamOps_CreatePlaybackStream_Call {179+func (_e *MockStreamOps_Expecter) CreatePlaybackStream(loopPtr interface{}, name interface{}, onProcess interface{}) *MockStreamOps_CreatePlaybackStream_Call {
182- return &MockStreamOps_CreatePlaybackStream_Call{Call: _e.mock.On("CreatePlaybackStream", loopPtr, name, sampleRate, channels, onProcess)}180+ return &MockStreamOps_CreatePlaybackStream_Call{Call: _e.mock.On("CreatePlaybackStream", loopPtr, name, onProcess)}
183 }181 }
184 182
185-func (_c *MockStreamOps_CreatePlaybackStream_Call) Run(run func(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func())) *MockStreamOps_CreatePlaybackStream_Call {183+func (_c *MockStreamOps_CreatePlaybackStream_Call) Run(run func(loopPtr unsafe.Pointer, name string, onProcess func())) *MockStreamOps_CreatePlaybackStream_Call {
186 _c.Call.Run(func(args mock.Arguments) {184 _c.Call.Run(func(args mock.Arguments) {
187 var arg0 unsafe.Pointer185 var arg0 unsafe.Pointer
188 if args[0] != nil {186 if args[0] != nil {
@@ -192,24 +190,14 @@ func (_c *MockStreamOps_CreatePlaybackStream_Call) Run(run func(loopPtr unsafe.P
192 if args[1] != nil {190 if args[1] != nil {
193 arg1 = args[1].(string)191 arg1 = args[1].(string)
194 }192 }
195- var arg2 int193+ var arg2 func()
196 if args[2] != nil {194 if args[2] != nil {
197- arg2 = args[2].(int)195+ arg2 = args[2].(func())
198- }
199- var arg3 int
200- if args[3] != nil {
201- arg3 = args[3].(int)
202- }
203- var arg4 func()
204- if args[4] != nil {
205- arg4 = args[4].(func())
206 }196 }
207 run(197 run(
208 arg0,198 arg0,
209 arg1,199 arg1,
210 arg2,200 arg2,
211- arg3,
212- arg4,
213 )201 )
214 })202 })
215 return _c203 return _c
@@ -220,7 +208,7 @@ func (_c *MockStreamOps_CreatePlaybackStream_Call) Return(streamPtr unsafe.Point
220 return _c208 return _c
221 }209 }
222 210
223-func (_c *MockStreamOps_CreatePlaybackStream_Call) RunAndReturn(run func(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func()) (unsafe.Pointer, error)) *MockStreamOps_CreatePlaybackStream_Call {211+func (_c *MockStreamOps_CreatePlaybackStream_Call) RunAndReturn(run func(loopPtr unsafe.Pointer, name string, onProcess func()) (unsafe.Pointer, error)) *MockStreamOps_CreatePlaybackStream_Call {
224 _c.Call.Return(run)212 _c.Call.Return(run)
225 return _c213 return _c
226 }214 }
M internal/ports/out/stream_ops.go +1 -1
@@ -9,7 +9,7 @@ type StreamOps interface {
99 // CreatePlaybackStream creates a new playback stream attached to the given
1010 // main loop. The onProcess callback is invoked by PipeWire each time the
1111 // stream needs more audio data. Returns the stream pointer or an error.
12- CreatePlaybackStream(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func()) (streamPtr unsafe.Pointer, err error)
12+ CreatePlaybackStream(loopPtr unsafe.Pointer, name string, onProcess func()) (streamPtr unsafe.Pointer, err error)
1313
1414 // ConnectPlaybackStream connects the stream for playback output.
1515 ConnectPlaybackStream(streamPtr unsafe.Pointer) error
@@ -9,7 +9,7 @@ type StreamOps interface {
9 // CreatePlaybackStream creates a new playback stream attached to the given9 // CreatePlaybackStream creates a new playback stream attached to the given
10 // main loop. The onProcess callback is invoked by PipeWire each time the10 // main loop. The onProcess callback is invoked by PipeWire each time the
11 // stream needs more audio data. Returns the stream pointer or an error.11 // stream needs more audio data. Returns the stream pointer or an error.
12- CreatePlaybackStream(loopPtr unsafe.Pointer, name string, sampleRate int, channels int, onProcess func()) (streamPtr unsafe.Pointer, err error)12+ CreatePlaybackStream(loopPtr unsafe.Pointer, name string, onProcess func()) (streamPtr unsafe.Pointer, err error)
13 13
14 // ConnectPlaybackStream connects the stream for playback output.14 // ConnectPlaybackStream connects the stream for playback output.
15 ConnectPlaybackStream(streamPtr unsafe.Pointer) error15 ConnectPlaybackStream(streamPtr unsafe.Pointer) error

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 8

↑↓ moveenter openesc close