11 #include "alcontext.h"
13 #include "alAuxEffectSlot.h"
14 #include "ringbuffer.h"
18 static int EventThread(ALCcontext
*context
)
20 RingBuffer
*ring
{context
->AsyncEvents
.get()};
22 while(LIKELY(!quitnow
))
24 auto evt_data
= ring
->getReadVector().first
;
27 context
->EventSem
.wait();
31 std::lock_guard
<std::mutex
> _
{context
->EventCbLock
};
33 auto &evt
= *reinterpret_cast<AsyncEvent
*>(evt_data
.buf
);
34 evt_data
.buf
+= sizeof(AsyncEvent
);
36 /* This automatically destructs the event object and advances the
37 * ringbuffer's read offset at the end of scope.
39 const struct EventAutoDestructor
{
42 ~EventAutoDestructor()
45 ring_
->readAdvance(1);
49 quitnow
= evt
.EnumType
== EventType_KillThread
;
50 if(UNLIKELY(quitnow
)) break;
52 if(evt
.EnumType
== EventType_ReleaseEffectState
)
54 evt
.u
.mEffectState
->DecRef();
58 ALbitfieldSOFT enabledevts
{context
->EnabledEvts
.load(std::memory_order_acquire
)};
59 if(!context
->EventCb
) continue;
61 if(evt
.EnumType
== EventType_SourceStateChange
)
63 if(!(enabledevts
&EventType_SourceStateChange
))
65 std::string msg
{"Source ID " + std::to_string(evt
.u
.srcstate
.id
)};
66 msg
+= " state has changed to ";
67 msg
+= (evt
.u
.srcstate
.state
==AL_INITIAL
) ? "AL_INITIAL" :
68 (evt
.u
.srcstate
.state
==AL_PLAYING
) ? "AL_PLAYING" :
69 (evt
.u
.srcstate
.state
==AL_PAUSED
) ? "AL_PAUSED" :
70 (evt
.u
.srcstate
.state
==AL_STOPPED
) ? "AL_STOPPED" : "<unknown>";
71 context
->EventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
, evt
.u
.srcstate
.id
,
72 evt
.u
.srcstate
.state
, static_cast<ALsizei
>(msg
.length()), msg
.c_str(),
76 else if(evt
.EnumType
== EventType_BufferCompleted
)
78 if(!(enabledevts
&EventType_BufferCompleted
))
80 std::string msg
{std::to_string(evt
.u
.bufcomp
.count
)};
81 if(evt
.u
.bufcomp
.count
== 1) msg
+= " buffer completed";
82 else msg
+= " buffers completed";
83 context
->EventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
, evt
.u
.bufcomp
.id
,
84 evt
.u
.bufcomp
.count
, static_cast<ALsizei
>(msg
.length()), msg
.c_str(),
88 else if((enabledevts
&evt
.EnumType
) == evt
.EnumType
)
89 context
->EventCb(evt
.u
.user
.type
, evt
.u
.user
.id
, evt
.u
.user
.param
,
90 static_cast<ALsizei
>(strlen(evt
.u
.user
.msg
)), evt
.u
.user
.msg
,
93 } while(evt_data
.len
!= 0);
98 void StartEventThrd(ALCcontext
*ctx
)
101 ctx
->EventThread
= std::thread(EventThread
, ctx
);
103 catch(std::exception
& e
) {
104 ERR("Failed to start event thread: %s\n", e
.what());
107 ERR("Failed to start event thread! Expect problems.\n");
111 void StopEventThrd(ALCcontext
*ctx
)
113 static constexpr AsyncEvent kill_evt
{EventType_KillThread
};
114 RingBuffer
*ring
{ctx
->AsyncEvents
.get()};
115 auto evt_data
= ring
->getWriteVector().first
;
116 if(evt_data
.len
== 0)
119 std::this_thread::yield();
120 evt_data
= ring
->getWriteVector().first
;
121 } while(evt_data
.len
== 0);
123 new (evt_data
.buf
) AsyncEvent
{kill_evt
};
124 ring
->writeAdvance(1);
126 ctx
->EventSem
.post();
127 if(ctx
->EventThread
.joinable())
128 ctx
->EventThread
.join();
131 AL_API
void AL_APIENTRY
alEventControlSOFT(ALsizei count
, const ALenum
*types
, ALboolean enable
)
133 ContextRef context
{GetContextRef()};
134 if(UNLIKELY(!context
)) return;
136 if(count
< 0) SETERR_RETURN(context
.get(), AL_INVALID_VALUE
,, "Controlling %d events", count
);
137 if(count
== 0) return;
138 if(!types
) SETERR_RETURN(context
.get(), AL_INVALID_VALUE
,, "NULL pointer");
140 ALbitfieldSOFT flags
{0};
141 const ALenum
*types_end
= types
+count
;
142 auto bad_type
= std::find_if_not(types
, types_end
,
143 [&flags
](ALenum type
) noexcept
-> bool
145 if(type
== AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
)
146 flags
|= EventType_BufferCompleted
;
147 else if(type
== AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
)
148 flags
|= EventType_SourceStateChange
;
149 else if(type
== AL_EVENT_TYPE_ERROR_SOFT
)
150 flags
|= EventType_Error
;
151 else if(type
== AL_EVENT_TYPE_PERFORMANCE_SOFT
)
152 flags
|= EventType_Performance
;
153 else if(type
== AL_EVENT_TYPE_DEPRECATED_SOFT
)
154 flags
|= EventType_Deprecated
;
155 else if(type
== AL_EVENT_TYPE_DISCONNECTED_SOFT
)
156 flags
|= EventType_Disconnected
;
162 if(bad_type
!= types_end
)
163 SETERR_RETURN(context
.get(), AL_INVALID_ENUM
,, "Invalid event type 0x%04x", *bad_type
);
167 ALbitfieldSOFT enabledevts
{context
->EnabledEvts
.load(std::memory_order_relaxed
)};
168 while(context
->EnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
|flags
,
169 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
171 /* enabledevts is (re-)filled with the current value on failure, so
178 ALbitfieldSOFT enabledevts
{context
->EnabledEvts
.load(std::memory_order_relaxed
)};
179 while(context
->EnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
&~flags
,
180 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
183 /* Wait to ensure the event handler sees the changed flags before
186 std::lock_guard
<std::mutex
>{context
->EventCbLock
};
190 AL_API
void AL_APIENTRY
alEventCallbackSOFT(ALEVENTPROCSOFT callback
, void *userParam
)
192 ContextRef context
{GetContextRef()};
193 if(UNLIKELY(!context
)) return;
195 std::lock_guard
<std::mutex
> _
{context
->PropLock
};
196 std::lock_guard
<std::mutex
> __
{context
->EventCbLock
};
197 context
->EventCb
= callback
;
198 context
->EventParam
= userParam
;