mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 07:14:56 +01:00
WebSockets: additional method for event loop integration
Some distro repositories offer versions of libwebsockets that have not been compiled with LWS_WITH_GLIB or LWS_WITH_EXTERNAL_POLL enabled. For such cases a different event loop integration method is needed. True for Ubuntu 20.04 as of Jun '21
This commit is contained in:
parent
a5a952be2a
commit
2bea7afcd4
2 changed files with 103 additions and 44 deletions
|
|
@ -52,6 +52,8 @@ using namespace ArdourSurface;
|
|||
WebsocketsServer::WebsocketsServer (ArdourSurface::ArdourWebsockets& surface)
|
||||
: SurfaceComponent (surface)
|
||||
, _lws_context (0)
|
||||
, _fd_callbacks (false)
|
||||
, _g_source (0)
|
||||
{
|
||||
/* keep references to all config for libwebsockets 2 */
|
||||
lws_protocols proto;
|
||||
|
|
@ -125,13 +127,28 @@ WebsocketsServer::start ()
|
|||
stop ();
|
||||
}
|
||||
|
||||
/* Event loop integration method depends on how libwebsockets is configured
|
||||
* for Ardour build environment and how libwebsockets is compiled for the
|
||||
* system running Ardour. */
|
||||
|
||||
#ifdef LWS_WITH_GLIB
|
||||
void *foreign_loops[1];
|
||||
foreign_loops[0] = main_loop ()->gobj ();
|
||||
_lws_info.foreign_loops = foreign_loops;
|
||||
_lws_info.options = LWS_SERVER_OPTION_GLIB;
|
||||
_lws_context = lws_create_context (&_lws_info);
|
||||
#endif
|
||||
|
||||
if (_lws_context) {
|
||||
/* LWS_WITH_GLIB was enabled for Ardour build environment libwebsockets
|
||||
and also for the version the user has installed in their system */
|
||||
PBD::info << "ArdourWebsockets: using event loop integration method 1" << endmsg;
|
||||
} else {
|
||||
/* Either Ardour build environment libwebsockets was not configured with
|
||||
LWS_WITH_GLIB enabled or user installed library is missing the feature */
|
||||
_fd_callbacks = true;
|
||||
_lws_info.foreign_loops = 0;
|
||||
_lws_info.options = 0;
|
||||
_lws_context = lws_create_context (&_lws_info);
|
||||
|
||||
if (!_lws_context) {
|
||||
|
|
@ -139,16 +156,17 @@ WebsocketsServer::start ()
|
|||
return -1;
|
||||
}
|
||||
|
||||
#ifndef LWS_WITH_GLIB
|
||||
/* sometimes LWS_WITH_EXTERNAL_POLL is missing from lws_config.h
|
||||
but the feature is still available, hence this runtime check */
|
||||
if (_fd_ctx.empty ()) {
|
||||
PBD::error << "ArdourWebsockets: check your libwebsockets was compiled"
|
||||
" with LWS_WITH_GLIB or LWS_WITH_EXTERNAL_POLL enabled"
|
||||
<< endmsg;
|
||||
return -1;
|
||||
if (!_fd_ctx.empty ()) {
|
||||
// LWS_CALLBACK_ADD_POLL_FD was called, LWS_WITH_EXTERNAL_POLL is available
|
||||
PBD::info << "ArdourWebsockets: using event loop integration method 2" << endmsg;
|
||||
} else {
|
||||
// Neither LWS_WITH_EXTERNAL_POLL or LWS_WITH_GLIB are available
|
||||
PBD::info << "ArdourWebsockets: using event loop integration method 3" << endmsg;
|
||||
_g_source = g_idle_source_new();
|
||||
g_source_set_callback (_g_source, WebsocketsServer::glib_idle_callback, _lws_context, 0);
|
||||
g_source_attach (_g_source, g_main_loop_get_context (main_loop ()->gobj ()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
PBD::info << "ArdourWebsockets: listening on: http://"
|
||||
<< lws_canonical_hostname (_lws_context)
|
||||
|
|
@ -163,7 +181,7 @@ WebsocketsServer::start ()
|
|||
int
|
||||
WebsocketsServer::stop ()
|
||||
{
|
||||
#ifndef LWS_WITH_GLIB
|
||||
if (!_fd_ctx.empty ()) { // Method 2
|
||||
for (LwsPollFdGlibSourceMap::iterator it = _fd_ctx.begin (); it != _fd_ctx.end (); ++it) {
|
||||
it->second.rg_iosrc->destroy ();
|
||||
|
||||
|
|
@ -173,7 +191,11 @@ WebsocketsServer::stop ()
|
|||
}
|
||||
|
||||
_fd_ctx.clear ();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_g_source) { // Method 3
|
||||
g_source_destroy (_g_source);
|
||||
}
|
||||
|
||||
if (_lws_context) {
|
||||
lws_context_destroy (_lws_context);
|
||||
|
|
@ -405,19 +427,30 @@ WebsocketsServer::lws_callback (struct lws* wsi, enum lws_callback_reasons reaso
|
|||
rc = server->send_availsurf_body (wsi);
|
||||
break;
|
||||
|
||||
#ifndef LWS_WITH_GLIB
|
||||
/* fd callbacks must be skipped for integration method 1 */
|
||||
case LWS_CALLBACK_ADD_POLL_FD:
|
||||
if (server->fd_callbacks()) {
|
||||
rc = server->add_poll_fd (static_cast<struct lws_pollargs*> (in));
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
|
||||
if (server->fd_callbacks()) {
|
||||
rc = server->mod_poll_fd (static_cast<struct lws_pollargs*> (in));
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_DEL_POLL_FD:
|
||||
if (server->fd_callbacks()) {
|
||||
rc = server->del_poll_fd (static_cast<struct lws_pollargs*> (in));
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
#endif // LWS_WITH_GLIB
|
||||
|
||||
#if LWS_LIBRARY_VERSION_NUM >= 2001000
|
||||
// lws_callback_http_dummy is not available on lws < 2.1.0
|
||||
|
|
@ -453,7 +486,6 @@ WebsocketsServer::lws_callback (struct lws* wsi, enum lws_callback_reasons reaso
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifndef LWS_WITH_GLIB
|
||||
int
|
||||
WebsocketsServer::add_poll_fd (struct lws_pollargs* pa)
|
||||
{
|
||||
|
|
@ -597,4 +629,18 @@ WebsocketsServer::ioc_to_events (IOCondition ioc)
|
|||
|
||||
return events;
|
||||
}
|
||||
#endif // LWS_WITH_GLIB
|
||||
|
||||
void
|
||||
WebsocketsServer::request_write ()
|
||||
{
|
||||
// cancel lws_service() in the idle callback to write pending data asap
|
||||
lws_cancel_service (_lws_context);
|
||||
}
|
||||
|
||||
gboolean
|
||||
WebsocketsServer::glib_idle_callback (void *data)
|
||||
{
|
||||
struct lws_context *lws_ctx = static_cast<struct lws_context *>(data);
|
||||
lws_service (lws_ctx, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,16 +39,6 @@
|
|||
// TO DO: make this configurable
|
||||
#define WEBSOCKET_LISTEN_PORT 3818
|
||||
|
||||
// lws includes integration with the glib event loop starting from v4
|
||||
#ifndef LWS_WITH_GLIB
|
||||
struct LwsPollFdGlibSource {
|
||||
struct lws_pollfd lws_pfd;
|
||||
Glib::RefPtr<Glib::IOChannel> g_channel;
|
||||
Glib::RefPtr<Glib::IOSource> rg_iosrc;
|
||||
Glib::RefPtr<Glib::IOSource> wg_iosrc;
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class WebsocketsServer : public SurfaceComponent
|
||||
|
|
@ -87,12 +77,24 @@ private:
|
|||
|
||||
static int lws_callback (struct lws*, enum lws_callback_reasons, void*, void*, size_t);
|
||||
|
||||
#ifndef LWS_WITH_GLIB
|
||||
/* Glib event loop integration that requires LWS_WITH_EXTERNAL_POLL */
|
||||
|
||||
struct LwsPollFdGlibSource {
|
||||
struct lws_pollfd lws_pfd;
|
||||
Glib::RefPtr<Glib::IOChannel> g_channel;
|
||||
Glib::RefPtr<Glib::IOSource> rg_iosrc;
|
||||
Glib::RefPtr<Glib::IOSource> wg_iosrc;
|
||||
};
|
||||
|
||||
Glib::RefPtr<Glib::IOChannel> _channel;
|
||||
|
||||
typedef boost::unordered_map<lws_sockfd_type, LwsPollFdGlibSource> LwsPollFdGlibSourceMap;
|
||||
LwsPollFdGlibSourceMap _fd_ctx;
|
||||
|
||||
bool _fd_callbacks;
|
||||
|
||||
bool fd_callbacks () { return _fd_callbacks; }
|
||||
|
||||
int add_poll_fd (struct lws_pollargs*);
|
||||
int mod_poll_fd (struct lws_pollargs*);
|
||||
int del_poll_fd (struct lws_pollargs*);
|
||||
|
|
@ -101,7 +103,18 @@ private:
|
|||
|
||||
Glib::IOCondition events_to_ioc (int);
|
||||
int ioc_to_events (Glib::IOCondition);
|
||||
#endif
|
||||
|
||||
/* Glib event loop integration that does NOT require LWS_WITH_EXTERNAL_POLL
|
||||
but needs a secondary thread for notifying the server when there is
|
||||
pending data for writing. Unfortunately libwesockets' own approach to
|
||||
Glib integration cannot be copied because it relies on file descriptors
|
||||
that are hidden by the 'lws' opaque type. See feedback.cc . */
|
||||
|
||||
GSource* _g_source;
|
||||
|
||||
void request_write ();
|
||||
static gboolean glib_idle_callback (void *data);
|
||||
|
||||
};
|
||||
|
||||
} // namespace ArdourSurface
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue