21 #include "alcontext.h"
24 #include "effects/base.h"
25 #include "inprogext.h"
27 #include "opthelpers.h"
28 #include "ringbuffer.h"
32 static int EventThread(ALCcontext
*context
)
34 RingBuffer
*ring
{context
->mAsyncEvents
.get()};
36 while LIKELY(!quitnow
)
38 auto evt_data
= ring
->getReadVector().first
;
41 context
->mEventSem
.wait();
45 std::lock_guard
<std::mutex
> _
{context
->mEventCbLock
};
47 auto *evt_ptr
= reinterpret_cast<AsyncEvent
*>(evt_data
.buf
);
48 evt_data
.buf
+= sizeof(AsyncEvent
);
51 AsyncEvent evt
{*evt_ptr
};
52 al::destroy_at(evt_ptr
);
55 quitnow
= evt
.EnumType
== EventType_KillThread
;
56 if UNLIKELY(quitnow
) break;
58 if(evt
.EnumType
== EventType_ReleaseEffectState
)
60 evt
.u
.mEffectState
->release();
64 ALbitfieldSOFT enabledevts
{context
->mEnabledEvts
.load(std::memory_order_acquire
)};
65 if(!context
->mEventCb
) continue;
67 if(evt
.EnumType
== EventType_SourceStateChange
)
69 if(!(enabledevts
&EventType_SourceStateChange
))
71 std::string msg
{"Source ID " + std::to_string(evt
.u
.srcstate
.id
)};
72 msg
+= " state has changed to ";
73 msg
+= (evt
.u
.srcstate
.state
==AL_INITIAL
) ? "AL_INITIAL" :
74 (evt
.u
.srcstate
.state
==AL_PLAYING
) ? "AL_PLAYING" :
75 (evt
.u
.srcstate
.state
==AL_PAUSED
) ? "AL_PAUSED" :
76 (evt
.u
.srcstate
.state
==AL_STOPPED
) ? "AL_STOPPED" : "<unknown>";
77 context
->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
, evt
.u
.srcstate
.id
,
78 static_cast<ALuint
>(evt
.u
.srcstate
.state
), static_cast<ALsizei
>(msg
.length()),
79 msg
.c_str(), context
->mEventParam
);
81 else if(evt
.EnumType
== EventType_BufferCompleted
)
83 if(!(enabledevts
&EventType_BufferCompleted
))
85 std::string msg
{std::to_string(evt
.u
.bufcomp
.count
)};
86 if(evt
.u
.bufcomp
.count
== 1) msg
+= " buffer completed";
87 else msg
+= " buffers completed";
88 context
->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
, evt
.u
.bufcomp
.id
,
89 evt
.u
.bufcomp
.count
, static_cast<ALsizei
>(msg
.length()), msg
.c_str(),
90 context
->mEventParam
);
92 else if((enabledevts
&evt
.EnumType
) == evt
.EnumType
)
93 context
->mEventCb(evt
.u
.user
.type
, evt
.u
.user
.id
, evt
.u
.user
.param
,
94 static_cast<ALsizei
>(strlen(evt
.u
.user
.msg
)), evt
.u
.user
.msg
,
95 context
->mEventParam
);
96 } while(evt_data
.len
!= 0);
101 void StartEventThrd(ALCcontext
*ctx
)
104 ctx
->mEventThread
= std::thread
{EventThread
, ctx
};
106 catch(std::exception
& e
) {
107 ERR("Failed to start event thread: %s\n", e
.what());
110 ERR("Failed to start event thread! Expect problems.\n");
114 void StopEventThrd(ALCcontext
*ctx
)
116 RingBuffer
*ring
{ctx
->mAsyncEvents
.get()};
117 auto evt_data
= ring
->getWriteVector().first
;
118 if(evt_data
.len
== 0)
121 std::this_thread::yield();
122 evt_data
= ring
->getWriteVector().first
;
123 } while(evt_data
.len
== 0);
125 new (evt_data
.buf
) AsyncEvent
{EventType_KillThread
};
126 ring
->writeAdvance(1);
128 ctx
->mEventSem
.post();
129 if(ctx
->mEventThread
.joinable())
130 ctx
->mEventThread
.join();
133 AL_API
void AL_APIENTRY
alEventControlSOFT(ALsizei count
, const ALenum
*types
, ALboolean enable
)
136 ContextRef context
{GetContextRef()};
137 if UNLIKELY(!context
) return;
139 if(count
< 0) context
->setError(AL_INVALID_VALUE
, "Controlling %d events", count
);
140 if(count
<= 0) return;
141 if(!types
) SETERR_RETURN(context
, AL_INVALID_VALUE
,, "NULL pointer");
143 ALbitfieldSOFT flags
{0};
144 const ALenum
*types_end
= types
+count
;
145 auto bad_type
= std::find_if_not(types
, types_end
,
146 [&flags
](ALenum type
) noexcept
-> bool
148 if(type
== AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
)
149 flags
|= EventType_BufferCompleted
;
150 else if(type
== AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
)
151 flags
|= EventType_SourceStateChange
;
152 else if(type
== AL_EVENT_TYPE_ERROR_SOFT
)
153 flags
|= EventType_Error
;
154 else if(type
== AL_EVENT_TYPE_PERFORMANCE_SOFT
)
155 flags
|= EventType_Performance
;
156 else if(type
== AL_EVENT_TYPE_DEPRECATED_SOFT
)
157 flags
|= EventType_Deprecated
;
158 else if(type
== AL_EVENT_TYPE_DISCONNECTED_SOFT
)
159 flags
|= EventType_Disconnected
;
165 if(bad_type
!= types_end
)
166 SETERR_RETURN(context
, AL_INVALID_ENUM
,, "Invalid event type 0x%04x", *bad_type
);
170 ALbitfieldSOFT enabledevts
{context
->mEnabledEvts
.load(std::memory_order_relaxed
)};
171 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
|flags
,
172 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
174 /* enabledevts is (re-)filled with the current value on failure, so
181 ALbitfieldSOFT enabledevts
{context
->mEnabledEvts
.load(std::memory_order_relaxed
)};
182 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
&~flags
,
183 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
186 /* Wait to ensure the event handler sees the changed flags before
189 std::lock_guard
<std::mutex
>{context
->mEventCbLock
};
194 AL_API
void AL_APIENTRY
alEventCallbackSOFT(ALEVENTPROCSOFT callback
, void *userParam
)
197 ContextRef context
{GetContextRef()};
198 if UNLIKELY(!context
) return;
200 std::lock_guard
<std::mutex
> _
{context
->mPropLock
};
201 std::lock_guard
<std::mutex
> __
{context
->mEventCbLock
};
202 context
->mEventCb
= callback
;
203 context
->mEventParam
= userParam
;