<commit_before>#include <iostream>
#include "util/path.h"

using namespace std;
using namespace zorba;

int main (int argc, const char **argv) {
  if (strcmp (argv [1], "--branch-path") == 0) {
    cout << filesystem_path (argv [2]).branch_path ().get_path () << endl;
  } else if (strcmp (argv [1], "--path") == 0) {
    cout << filesystem_path (argv [2]).get_path () << endl;
  }
}
<commit_msg>Fixed an include<commit_after>#include <iostream>
#include <string.h>
#include "util/path.h"

using namespace std;
using namespace zorba;

int main (int argc, const char **argv) {
  if (strcmp (argv [1], "--branch-path") == 0) {
    cout << filesystem_path (argv [2]).branch_path ().get_path () << endl;
  } else if (strcmp (argv [1], "--path") == 0) {
    cout << filesystem_path (argv [2]).get_path () << endl;
  }
}
<|endoftext|>


<commit_before>void sim(Int_t nev=20) {
  AliSimulation simulator;
  simulator.SetMakeSDigits("TRD TOF PHOS HMPID EMCAL MUON FMD ZDC PMD T0 VZERO");
  simulator.SetMakeDigitsFromHits("ITS TPC");
  simulator.SetWriteRawData("ALL","raw.root",kTRUE);

  simulator.SetDefaultStorage("local://$ALICE_ROOT");
  simulator.SetSpecificStorage("GRP/GRP/Data",
			       Form("local://%s",gSystem->pwd()));
  simulator.SetRunQA("ALL:ALL") ;
  AliQA::SetQARefStorage("local://$ALICE_ROOT") ;

  for (Int_t det = 0 ; det < AliQA::kNDET ; det++) {
    simulator.SetQACycles(det, nev+1) ;
  }

  TStopwatch timer;
  timer.Start();
  simulator.Run(nev);
  timer.Stop();
  timer.Print();
}
<commit_msg>allow to set the number of events throub an env variable<commit_after>void sim(Int_t nev=1) {
  AliSimulation simulator;
  simulator.SetMakeSDigits("TRD TOF PHOS HMPID EMCAL MUON FMD ZDC PMD T0 VZERO");
  simulator.SetMakeDigitsFromHits("ITS TPC");
  simulator.SetWriteRawData("ALL","raw.root",kTRUE);

  simulator.SetDefaultStorage("local://$ALICE_ROOT");
  simulator.SetSpecificStorage("GRP/GRP/Data",
			       Form("local://%s",gSystem->pwd()));
  simulator.SetRunQA("ALL:ALL") ;
  AliQA::SetQARefStorage("local://$ALICE_ROOT") ;

  for (Int_t det = 0 ; det < AliQA::kNDET ; det++) {
    simulator.SetQACycles(det, nev+1) ;
  }

  TStopwatch timer;
  timer.Start();
  simulator.Run(nev);
  timer.Stop();
  timer.Print();
}
<|endoftext|>


<commit_before>#include "context.h"

#include <QAbstractEventDispatcher>
#include <QDebug>

#warning todo this needs to be a singleton as multiple contexts dont make sense and it makes it eaier to use from qml

static bool isGoodState(int eol)
{
    if (eol < 0) {
        // Error
        return false;
    }

    if (eol > 0) {
        // End of callback chain
        return false;
    }

    return true;
}

// --------------------------

static void sink_cb(pa_context *context, const pa_sink_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->sinkCallback(info);
}

static void sink_input_callback(pa_context *context, const pa_sink_input_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->sinkInputCallback(info);
}

static void client_cb(pa_context *context, const pa_client_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->clientCallback(info);
}

static void context_state_callback(pa_context *context, void *data)
{
    Q_ASSERT(data);
    ((Context *)data)->contextStateCallback(context);
}

static void subscribe_cb(pa_context *context, pa_subscription_event_type_t type, uint32_t index, void *data)
{
    Q_ASSERT(data);
    ((Context *)data)->subscribeCallback(context, type, index);
}

// --------------------------

Context::Context(QObject *parent)
    : QObject(parent)
    , m_context(nullptr)
    , m_mainloop(nullptr)
{
    // We require a glib event loop
    if (!QByteArray(QAbstractEventDispatcher::instance()->metaObject()->className()).contains("EventDispatcherGlib")) {
        qDebug() << "Disabling PulseAudio integration for lack of GLib event loop";
        return;
    }

    connectToDaemon();
}

Context::~Context()
{
    pa_context_unref(m_context);
    pa_glib_mainloop_free(m_mainloop);
}

void Context::subscribeCallback(pa_context *context, pa_subscription_event_type_t type, uint32_t index)
{
#warning fixme
//    Q_ASSERT(c == s_context);

    switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
#warning fixme
        case PA_SUBSCRIPTION_EVENT_SINK:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "DEL Sink" << index;
                if (!m_sinks.contains(index)) {
                    m_recentlyDeletedSinks.insert(index);
                } else {
                    const int modelIndex = m_sinks.keys().indexOf(index);
                    m_sinks.take(index)->deleteLater();
                    emit sinkRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_info_by_index(context, index, sink_cb, this))) {
                    qWarning() << "pa_context_get_sink_info_by_index() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

//        case PA_SUBSCRIPTION_EVENT_SOURCE:
//            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
//                if (s_mixers.contains(KMIXPA_CAPTURE))
//                    s_mixers[KMIXPA_CAPTURE]->removeWidget(index);
//            } else {
//                pa_operation *o;
//                if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, NULL))) {
//                    qWarning() << "pa_context_get_source_info_by_index() failed";
//                    return;
//                }
//                pa_operation_unref(o);
//            }
//            break;

        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << Q_FUNC_INFO << "::: dropping :::" << index;
                if (!m_sinkInputs.contains(index)) {
                    m_recentlyDeletedSinkInputs.insert(index);
                } else {
                    const int modelIndex = m_sinkInputs.keys().indexOf(index);
                    m_sinkInputs.take(index)->deleteLater();
                    emit sinkInputRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_input_info(context, index, sink_input_callback, this))) {
                    qWarning() << "pa_context_get_sink_input_info() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

//        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
//            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
//                if (s_mixers.contains(KMIXPA_APP_CAPTURE))
//                    s_mixers[KMIXPA_APP_CAPTURE]->removeWidget(index);
//            } else {
//                pa_operation *o;
//                if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, NULL))) {
//                    qWarning() << "pa_context_get_sink_input_info() failed";
//                    return;
//                }
//                pa_operation_unref(o);
//            }
//            break;

        case PA_SUBSCRIPTION_EVENT_CLIENT:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "dropping client" << index;
                if (!m_clients.contains(index)) {
                    m_recentDeletedClients.insert(index);
                } else {
                    const int modelIndex = m_clients.keys().indexOf(index);
                    m_clients.take(index)->deleteLater();
                    emit clientRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_client_info(context, index, client_cb, this))) {
                    qWarning() << "pa_context_get_client_info() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

    }
}

void Context::contextStateCallback(pa_context *c)
{
    qDebug() << "state callback";
    pa_context_state_t state = pa_context_get_state(c);
    if (state == PA_CONTEXT_READY) {
        qDebug() << "ready";
        // Attempt to load things up
        pa_operation *o = nullptr;

        // 1. Register for the stream changes (except during probe)
        if (m_context == c) {
#warning fixme
            pa_context_set_subscribe_callback(c, subscribe_cb, this);

            if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
                                           (PA_SUBSCRIPTION_MASK_SINK|
                                            PA_SUBSCRIPTION_MASK_SOURCE|
                                            PA_SUBSCRIPTION_MASK_CLIENT|
                                            PA_SUBSCRIPTION_MASK_SINK_INPUT|
                                            PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) {
                qWarning() << "pa_context_subscribe() failed";
                return;
            }
            pa_operation_unref(o);
        }

#warning fixme
        if (!(o = pa_context_get_sink_info_list(c, sink_cb, this))) {
            qWarning() << "pa_context_get_sink_info_list() failed";
            return;
        }
        pa_operation_unref(o);

//        if (!(o = pa_context_get_source_info_list(c, source_cb, NULL))) {
//            qWarning() << "pa_context_get_source_info_list() failed";
//            return;
//        }
//        pa_operation_unref(o);

        if (!(o = pa_context_get_client_info_list(c, client_cb, this))) {
            qWarning() << "pa_context_client_info_list() failed";
            return;
        }
        pa_operation_unref(o);

        if (!(o = pa_context_get_sink_input_info_list(c, sink_input_callback, this))) {
            qWarning() << "pa_context_get_sink_input_info_list() failed";
            return;
        }
        pa_operation_unref(o);

//        if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, NULL))) {
//            qWarning() << "pa_context_get_source_output_info_list() failed";
//            return;
//        }
//        pa_operation_unref(o);

        /* These calls are not always supported */
//        if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, NULL))) {
//            pa_operation_unref(o);

//            pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, NULL);

//            if ((o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL)))
//                pa_operation_unref(o);
//        } else {
//            qWarning() << "Failed to initialize stream_restore extension: " << pa_strerror(pa_context_errno(m_context));
//        }
    } else if (!PA_CONTEXT_IS_GOOD(state)) {
        qDebug() << "context kaput";
        // If we're not probing, it means we've been disconnected from our
        // glib context
        pa_context_unref(m_context);
        m_context = nullptr;
#warning fixme need to clear out all our data and possibly reconnect
    }
}

template <typename C, typename PAInfo, typename AddedSignal, typename UpdatedSignal>
void Context::updateMap(QMap<quint32, C *> &map,
                        QSet<quint32> &exclusionSet,
                        const PAInfo *info,
                        AddedSignal addedSignal,
                        UpdatedSignal updatedSignal)
{
    Q_ASSERT(info);

    if (exclusionSet.remove(info->index)) {
        // Was already removed again.
        return;
    }

    const bool isNew = !map.contains(info->index);

    auto *obj = map.value(info->index, nullptr);
    if (!obj) {
        obj = new C;
    }
    obj->setInfo(info);
    map.insert(info->index, obj);

    const int modelIndex = map.keys().indexOf(info->index);
    Q_ASSERT(modelIndex >= 0);

    if (isNew) {
        emit (this->*addedSignal)(modelIndex);
    } else {
        emit (this->*updatedSignal)(modelIndex);
    }
}

#warning fixme recentlydeleted has the same type for everything making it easy to use the wrong one possibly
void Context::sinkCallback(const pa_sink_info *info)
{
    updateMap<Sink, pa_sink_info>(m_sinks, m_recentlyDeletedSinks,
                                  info,
                                  &Context::sinkAdded,
                                  &Context::sinkUpdated);
}

void Context::clientCallback(const pa_client_info *info)
{
    updateMap<Client, pa_client_info>(m_clients, m_recentDeletedClients,
                                      info,
                                      &Context::clientAdded,
                                      &Context::clientUpdated);
}

void Context::sinkInputCallback(const pa_sink_input_info *info)
{
    updateMap<SinkInput, pa_sink_input_info>(m_sinkInputs, m_recentlyDeletedSinkInputs,
                                             info,
                                             &Context::sinkInputAdded,
                                             &Context::sinkInputUpdated);
}

void Context::setSinkVolume(quint32 index, quint32 volume)
{
    qDebug() << Q_FUNC_INFO << index << volume;
#warning fixme volume limit enforcement needs review for sensibility
    if (volume > 65536)
        return;
    Sink *sink = m_sinks.value(index, nullptr);
    if (!sink)
        return;
    pa_cvolume newVolume = sink->volume();
    for (int i = 0; i < newVolume.channels; ++i) {
        newVolume.values[i] = volume;
    }
    pa_operation *o;
    if (!(o = pa_context_set_sink_volume_by_index(m_context, index, &newVolume, NULL, NULL))) {
        qWarning() <<  "pa_context_set_sink_volume_by_index() failed";
        return;
    }
    pa_operation_unref(o);
}

void Context::setSinkPort(quint32 portIndex)
{
    pa_operation *o;
    if (!(o = pa_context_set_sink_port_by_index(m_context, portIndex, nullptr, nullptr, nullptr))) {
        qWarning() << "pa_context_set_sink_port_by_index failed";
        return;
    }
}

void Context::setSinkInputVolume(quint32 index, quint32 volume)
{
    qDebug() << Q_FUNC_INFO << index << volume;
#warning fixme volume limit enforcement needs review for sensibility also this prevents overdrive
    if (volume > 65536)
        volume = 0;
    pa_operation *o;
    SinkInput *obj = m_sinkInputs.value(index, nullptr);
    if (!obj)
        return;
    pa_cvolume newVolume = obj->volume();
    for (int i = 0; i < newVolume.channels; ++i) {
        newVolume.values[i] = volume;
    }
    if (!(o = pa_context_set_sink_input_volume(m_context, index, &newVolume, NULL, NULL))) {
        qWarning() <<  "pa_context_set_sink_volume_by_index() failed";
        return;
    }
    pa_operation_unref(o);
}

void Context::connectToDaemon()
{
    Q_ASSERT(m_context == nullptr);
    Q_ASSERT(m_mainloop == nullptr);

    qDebug() <<  "Attempting connection to PulseAudio sound daemon";
    m_mainloop = pa_glib_mainloop_new(nullptr);
    pa_mainloop_api *api = pa_glib_mainloop_get_api(m_mainloop);
    Q_ASSERT(api);

    m_context = pa_context_new(api, "QPulse");
    Q_ASSERT(m_context);

    if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOFAIL, 0) < 0) {
        pa_context_unref(m_context);
        m_context = nullptr;
        return;
    }
    pa_context_set_state_callback(m_context, &context_state_callback, this);
}
<commit_msg>align pa_op location<commit_after>#include "context.h"

#include <QAbstractEventDispatcher>
#include <QDebug>

#warning todo this needs to be a singleton as multiple contexts dont make sense and it makes it eaier to use from qml

static bool isGoodState(int eol)
{
    if (eol < 0) {
        // Error
        return false;
    }

    if (eol > 0) {
        // End of callback chain
        return false;
    }

    return true;
}

// --------------------------

static void sink_cb(pa_context *context, const pa_sink_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->sinkCallback(info);
}

static void sink_input_callback(pa_context *context, const pa_sink_input_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->sinkInputCallback(info);
}

static void client_cb(pa_context *context, const pa_client_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->clientCallback(info);
}

static void context_state_callback(pa_context *context, void *data)
{
    Q_ASSERT(data);
    ((Context *)data)->contextStateCallback(context);
}

static void subscribe_cb(pa_context *context, pa_subscription_event_type_t type, uint32_t index, void *data)
{
    Q_ASSERT(data);
    ((Context *)data)->subscribeCallback(context, type, index);
}

// --------------------------

Context::Context(QObject *parent)
    : QObject(parent)
    , m_context(nullptr)
    , m_mainloop(nullptr)
{
    // We require a glib event loop
    if (!QByteArray(QAbstractEventDispatcher::instance()->metaObject()->className()).contains("EventDispatcherGlib")) {
        qDebug() << "Disabling PulseAudio integration for lack of GLib event loop";
        return;
    }

    connectToDaemon();
}

Context::~Context()
{
    pa_context_unref(m_context);
    pa_glib_mainloop_free(m_mainloop);
}

void Context::subscribeCallback(pa_context *context, pa_subscription_event_type_t type, uint32_t index)
{
#warning fixme
//    Q_ASSERT(c == s_context);

    switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
#warning fixme
        case PA_SUBSCRIPTION_EVENT_SINK:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "DEL Sink" << index;
                if (!m_sinks.contains(index)) {
                    m_recentlyDeletedSinks.insert(index);
                } else {
                    const int modelIndex = m_sinks.keys().indexOf(index);
                    m_sinks.take(index)->deleteLater();
                    emit sinkRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_info_by_index(context, index, sink_cb, this))) {
                    qWarning() << "pa_context_get_sink_info_by_index() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

//        case PA_SUBSCRIPTION_EVENT_SOURCE:
//            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
//                if (s_mixers.contains(KMIXPA_CAPTURE))
//                    s_mixers[KMIXPA_CAPTURE]->removeWidget(index);
//            } else {
//                pa_operation *o;
//                if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, NULL))) {
//                    qWarning() << "pa_context_get_source_info_by_index() failed";
//                    return;
//                }
//                pa_operation_unref(o);
//            }
//            break;

        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << Q_FUNC_INFO << "::: dropping :::" << index;
                if (!m_sinkInputs.contains(index)) {
                    m_recentlyDeletedSinkInputs.insert(index);
                } else {
                    const int modelIndex = m_sinkInputs.keys().indexOf(index);
                    m_sinkInputs.take(index)->deleteLater();
                    emit sinkInputRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_input_info(context, index, sink_input_callback, this))) {
                    qWarning() << "pa_context_get_sink_input_info() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

//        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
//            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
//                if (s_mixers.contains(KMIXPA_APP_CAPTURE))
//                    s_mixers[KMIXPA_APP_CAPTURE]->removeWidget(index);
//            } else {
//                pa_operation *o;
//                if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, NULL))) {
//                    qWarning() << "pa_context_get_sink_input_info() failed";
//                    return;
//                }
//                pa_operation_unref(o);
//            }
//            break;

        case PA_SUBSCRIPTION_EVENT_CLIENT:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "dropping client" << index;
                if (!m_clients.contains(index)) {
                    m_recentDeletedClients.insert(index);
                } else {
                    const int modelIndex = m_clients.keys().indexOf(index);
                    m_clients.take(index)->deleteLater();
                    emit clientRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_client_info(context, index, client_cb, this))) {
                    qWarning() << "pa_context_get_client_info() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

    }
}

void Context::contextStateCallback(pa_context *c)
{
    qDebug() << "state callback";
    pa_context_state_t state = pa_context_get_state(c);
    if (state == PA_CONTEXT_READY) {
        qDebug() << "ready";
        // Attempt to load things up
        pa_operation *o = nullptr;

        // 1. Register for the stream changes (except during probe)
        if (m_context == c) {
#warning fixme
            pa_context_set_subscribe_callback(c, subscribe_cb, this);

            if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
                                           (PA_SUBSCRIPTION_MASK_SINK|
                                            PA_SUBSCRIPTION_MASK_SOURCE|
                                            PA_SUBSCRIPTION_MASK_CLIENT|
                                            PA_SUBSCRIPTION_MASK_SINK_INPUT|
                                            PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) {
                qWarning() << "pa_context_subscribe() failed";
                return;
            }
            pa_operation_unref(o);
        }

#warning fixme
        if (!(o = pa_context_get_sink_info_list(c, sink_cb, this))) {
            qWarning() << "pa_context_get_sink_info_list() failed";
            return;
        }
        pa_operation_unref(o);

//        if (!(o = pa_context_get_source_info_list(c, source_cb, NULL))) {
//            qWarning() << "pa_context_get_source_info_list() failed";
//            return;
//        }
//        pa_operation_unref(o);

        if (!(o = pa_context_get_client_info_list(c, client_cb, this))) {
            qWarning() << "pa_context_client_info_list() failed";
            return;
        }
        pa_operation_unref(o);

        if (!(o = pa_context_get_sink_input_info_list(c, sink_input_callback, this))) {
            qWarning() << "pa_context_get_sink_input_info_list() failed";
            return;
        }
        pa_operation_unref(o);

//        if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, NULL))) {
//            qWarning() << "pa_context_get_source_output_info_list() failed";
//            return;
//        }
//        pa_operation_unref(o);

        /* These calls are not always supported */
//        if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, NULL))) {
//            pa_operation_unref(o);

//            pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, NULL);

//            if ((o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL)))
//                pa_operation_unref(o);
//        } else {
//            qWarning() << "Failed to initialize stream_restore extension: " << pa_strerror(pa_context_errno(m_context));
//        }
    } else if (!PA_CONTEXT_IS_GOOD(state)) {
        qDebug() << "context kaput";
        // If we're not probing, it means we've been disconnected from our
        // glib context
        pa_context_unref(m_context);
        m_context = nullptr;
#warning fixme need to clear out all our data and possibly reconnect
    }
}

template <typename C, typename PAInfo, typename AddedSignal, typename UpdatedSignal>
void Context::updateMap(QMap<quint32, C *> &map,
                        QSet<quint32> &exclusionSet,
                        const PAInfo *info,
                        AddedSignal addedSignal,
                        UpdatedSignal updatedSignal)
{
    Q_ASSERT(info);

    if (exclusionSet.remove(info->index)) {
        // Was already removed again.
        return;
    }

    const bool isNew = !map.contains(info->index);

    auto *obj = map.value(info->index, nullptr);
    if (!obj) {
        obj = new C;
    }
    obj->setInfo(info);
    map.insert(info->index, obj);

    const int modelIndex = map.keys().indexOf(info->index);
    Q_ASSERT(modelIndex >= 0);

    if (isNew) {
        emit (this->*addedSignal)(modelIndex);
    } else {
        emit (this->*updatedSignal)(modelIndex);
    }
}

#warning fixme recentlydeleted has the same type for everything making it easy to use the wrong one possibly
void Context::sinkCallback(const pa_sink_info *info)
{
    updateMap<Sink, pa_sink_info>(m_sinks, m_recentlyDeletedSinks,
                                  info,
                                  &Context::sinkAdded,
                                  &Context::sinkUpdated);
}

void Context::clientCallback(const pa_client_info *info)
{
    updateMap<Client, pa_client_info>(m_clients, m_recentDeletedClients,
                                      info,
                                      &Context::clientAdded,
                                      &Context::clientUpdated);
}

void Context::sinkInputCallback(const pa_sink_input_info *info)
{
    updateMap<SinkInput, pa_sink_input_info>(m_sinkInputs, m_recentlyDeletedSinkInputs,
                                             info,
                                             &Context::sinkInputAdded,
                                             &Context::sinkInputUpdated);
}

void Context::setSinkVolume(quint32 index, quint32 volume)
{
    qDebug() << Q_FUNC_INFO << index << volume;
#warning fixme volume limit enforcement needs review for sensibility
    if (volume > 65536)
        return;
    Sink *sink = m_sinks.value(index, nullptr);
    if (!sink)
        return;
    pa_cvolume newVolume = sink->volume();
    for (int i = 0; i < newVolume.channels; ++i) {
        newVolume.values[i] = volume;
    }
    pa_operation *o;
    if (!(o = pa_context_set_sink_volume_by_index(m_context, index, &newVolume, NULL, NULL))) {
        qWarning() <<  "pa_context_set_sink_volume_by_index() failed";
        return;
    }
    pa_operation_unref(o);
}

void Context::setSinkPort(quint32 portIndex)
{
    pa_operation *o;
    if (!(o = pa_context_set_sink_port_by_index(m_context, portIndex, nullptr, nullptr, nullptr))) {
        qWarning() << "pa_context_set_sink_port_by_index failed";
        return;
    }
}

void Context::setSinkInputVolume(quint32 index, quint32 volume)
{
    qDebug() << Q_FUNC_INFO << index << volume;
#warning fixme volume limit enforcement needs review for sensibility also this prevents overdrive
    if (volume > 65536)
        volume = 0;
    SinkInput *obj = m_sinkInputs.value(index, nullptr);
    if (!obj)
        return;
    pa_cvolume newVolume = obj->volume();
    for (int i = 0; i < newVolume.channels; ++i) {
        newVolume.values[i] = volume;
    }
    pa_operation *o;
    if (!(o = pa_context_set_sink_input_volume(m_context, index, &newVolume, NULL, NULL))) {
        qWarning() <<  "pa_context_set_sink_volume_by_index() failed";
        return;
    }
    pa_operation_unref(o);
}

void Context::connectToDaemon()
{
    Q_ASSERT(m_context == nullptr);
    Q_ASSERT(m_mainloop == nullptr);

    qDebug() <<  "Attempting connection to PulseAudio sound daemon";
    m_mainloop = pa_glib_mainloop_new(nullptr);
    pa_mainloop_api *api = pa_glib_mainloop_get_api(m_mainloop);
    Q_ASSERT(api);

    m_context = pa_context_new(api, "QPulse");
    Q_ASSERT(m_context);

    if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOFAIL, 0) < 0) {
        pa_context_unref(m_context);
        m_context = nullptr;
        return;
    }
    pa_context_set_state_callback(m_context, &context_state_callback, this);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2016 Daniele Bartolini and individual contributors.
 * License: https://github.com/taylor001/crown/blob/master/LICENSE
 */

#include "dynamic_string.h"
#include "os.h"
#include "string_stream.h"
#include "temp_allocator.h"
#include "vector.h"
#include <string.h> // strcmp

#if CROWN_PLATFORM_POSIX
	#include <dirent.h> // opendir, readdir
#endif

namespace crown
{
namespace os
{
	void list_files(const char* path, Vector<DynamicString>& files)
	{
#if CROWN_PLATFORM_POSIX
		DIR *dir;
		struct dirent *entry;

		if (!(dir = opendir(path)))
			return;

		while ((entry = readdir(dir)))
		{
			const char* dname = entry->d_name;

			if (!strcmp(dname, ".") || !strcmp(dname, ".."))
				continue;

			TempAllocator512 ta;
			DynamicString fname(ta);
			fname.set(dname, strlen32(dname));
			vector::push_back(files, fname);
		}

		closedir(dir);
#elif CROWN_PLATFORM_WINDOWS
		TempAllocator1024 ta;
		DynamicString cur_path(ta);
		cur_path += path;
		cur_path += "\\*";

		WIN32_FIND_DATA ffd;
		HANDLE file = FindFirstFile(cur_path.c_str(), &ffd);
		if (file == INVALID_HANDLE_VALUE)
			return;

		do
		{
			const char* dname = ffd.cFileName;

			if (!strcmp(dname, ".") || !strcmp(dname, ".."))
				continue;

			TempAllocator512 ta;
			DynamicString fname(ta);
			fname.set(dname, strlen32(dname));
			vector::push_back(files, fname);
		}
		while (FindNextFile(file, &ffd) != 0);

		FindClose(file);
#endif
	}

	int execute_process(const char* path, const char* args, StringStream& output)
	{
#if CROWN_PLATFORM_POSIX
		TempAllocator512 ta;
		DynamicString cmd(ta);
		cmd += path;
		cmd += " 2>&1 ";
		cmd += args;
		FILE* file = popen(cmd.c_str(), "r");

		char buf[1024];
		while (fgets(buf, sizeof(buf), file) != NULL)
			output << buf;

		return pclose(file);
#elif CROWN_PLATFORM_WINDOWS
		STARTUPINFO info;
		memset(&info, 0, sizeof(info));
		info.cb = sizeof(info);

		PROCESS_INFORMATION process;
		memset(&process, 0, sizeof(process));

		int err = CreateProcess(path, (LPSTR)args, NULL, NULL, TRUE, 0, NULL, NULL, &info, &process);
		CE_ASSERT(err != 0, "CreateProcess: GetLastError = %d", GetLastError());
		CE_UNUSED(err);

		DWORD exitcode = 1;
		::WaitForSingleObject(process.hProcess, INFINITE);
  		GetExitCodeProcess(process.hProcess, &exitcode);
		CloseHandle(process.hProcess);
		CloseHandle(process.hThread);
		return (int)exitcode;
#endif
	}
} // namespace os

} // namespace crown
<commit_msg>Cleanup<commit_after>/*
 * Copyright (c) 2012-2016 Daniele Bartolini and individual contributors.
 * License: https://github.com/taylor001/crown/blob/master/LICENSE
 */

#include "dynamic_string.h"
#include "os.h"
#include "string_stream.h"
#include "temp_allocator.h"
#include "vector.h"
#include <string.h> // strcmp

#if CROWN_PLATFORM_POSIX
	#include <dirent.h> // opendir, readdir
#endif

namespace crown
{
namespace os
{
	void list_files(const char* path, Vector<DynamicString>& files)
	{
#if CROWN_PLATFORM_POSIX
		DIR *dir;
		struct dirent *entry;

		if (!(dir = opendir(path)))
			return;

		while ((entry = readdir(dir)))
		{
			const char* dname = entry->d_name;

			if (!strcmp(dname, ".") || !strcmp(dname, ".."))
				continue;

			TempAllocator512 ta;
			DynamicString fname(ta);
			fname.set(dname, strlen32(dname));
			vector::push_back(files, fname);
		}

		closedir(dir);
#elif CROWN_PLATFORM_WINDOWS
		TempAllocator1024 ta;
		DynamicString cur_path(ta);
		cur_path += path;
		cur_path += "\\*";

		WIN32_FIND_DATA ffd;
		HANDLE file = FindFirstFile(cur_path.c_str(), &ffd);
		if (file == INVALID_HANDLE_VALUE)
			return;

		do
		{
			const char* dname = ffd.cFileName;

			if (!strcmp(dname, ".") || !strcmp(dname, ".."))
				continue;

			TempAllocator512 ta;
			DynamicString fname(ta);
			fname.set(dname, strlen32(dname));
			vector::push_back(files, fname);
		}
		while (FindNextFile(file, &ffd) != 0);

		FindClose(file);
#endif
	}

	int execute_process(const char* path, const char* args, StringStream& output)
	{
#if CROWN_PLATFORM_POSIX
		TempAllocator512 ta;
		DynamicString cmd(ta);
		cmd += path;
		cmd += " 2>&1 ";
		cmd += args;
		FILE* file = popen(cmd.c_str(), "r");

		char buf[1024];
		while (fgets(buf, sizeof(buf), file) != NULL)
			output << buf;

		return pclose(file);
#elif CROWN_PLATFORM_WINDOWS
		STARTUPINFO info;
		memset(&info, 0, sizeof(info));
		info.cb = sizeof(info);

		PROCESS_INFORMATION process;
		memset(&process, 0, sizeof(process));

		int err = CreateProcess(path, (LPSTR)args, NULL, NULL, TRUE, 0, NULL, NULL, &info, &process);
		CE_ASSERT(err != 0, "CreateProcess: GetLastError = %d", GetLastError());
		CE_UNUSED(err);

		DWORD exitcode = 1;
		::WaitForSingleObject(process.hProcess, INFINITE);
		GetExitCodeProcess(process.hProcess, &exitcode);
		CloseHandle(process.hProcess);
		CloseHandle(process.hThread);
		return (int)exitcode;
#endif
	}
} // namespace os

} // namespace crown
<|endoftext|>


<commit_before>/*

Copyright (c) 2014 Jarryd Beck

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

#include <regex>
#include <set>
#include <map>
#include <exception>
#include <sstream>

namespace cxxopts
{
  class Value
  {
    public:

    virtual void
    parse(const std::string& text) const = 0;

    virtual bool
    has_arg() const = 0;
  };

  namespace values
  {
    template <typename T>
    void
    parse_value(const std::string& text, T& value)
    {
      std::istringstream is(text);
      is >> value;
    }

    template <typename T>
    void
    parse_value(const std::string& text, std::vector<T>& value)
    {
      T v;
      parse_value(text, v);
      value.push_back(v);
    }

    template <typename T>
    struct value_has_arg
    {
      static constexpr bool value = true;
    };

    template <>
    struct value_has_arg<bool>
    {
      static constexpr bool value = false;
    };

    template <typename T>
    class default_value : public Value
    {
      public:
      default_value()
      : m_result(std::make_shared<T>())
      , m_store(m_result.get())
      {
      }

      default_value(T* t)
      : m_store(t)
      {
      }

      void
      parse(const std::string& text) const
      {
        parse_value(text, *m_store);
      }

      bool
      has_arg() const
      {
        return value_has_arg<T>::value;
      }

      const T&
      get() const
      {
        if (m_store == nullptr)
        {
          return *m_result;
        }
        else
        {
          return *m_store;
        }
      }

      private:
      std::shared_ptr<T> m_result;
      T* m_store;
    };

  }

  template <typename T>
  std::shared_ptr<Value>
  value()
  {
    return std::make_shared<values::default_value<T>>();
  }

  class OptionException : public std::exception
  {
    public:
    OptionException(const std::string& message)
    : m_message(message)
    {
    }

    virtual const char*
    what() const noexcept
    {
      return m_message.c_str();
    }

    private:
    std::string m_message;
  };

  class OptionSpecException : public OptionException
  {
    public:

    OptionSpecException(const std::string& message)
    : OptionException(message)
    {
    }
  };

  class OptionParseException : public OptionException
  {
    public:
    OptionParseException(const std::string& message)
    : OptionException(message)
    {
    }
  };

  class option_exists_error : public OptionSpecException
  {
    public:
    option_exists_error(const std::string& option)
    : OptionSpecException(u8"Option ‘" + option + u8"’ already exists")
    {
    }
  };

  class invalid_option_format_error : public OptionSpecException
  {
    public:
    invalid_option_format_error(const std::string& format)
    : OptionSpecException(u8"Invalid option format ‘" + format + u8"’")
    {
    }
  };

  class option_not_exists_exception : public OptionParseException
  {
    public:
    option_not_exists_exception(const std::string& option)
    : OptionParseException(u8"Option ‘" + option + u8"’ does not exist")
    {
    }
  };

  class missing_argument_exception : public OptionParseException
  {
    public:
    missing_argument_exception(const std::string& option)
    : OptionParseException(u8"Option ‘" + option + u8"’ is missing an argument")
    {
    }
  };

  class option_requires_argument_exception : public OptionParseException
  {
    public:
    option_requires_argument_exception(const std::string& option)
    : OptionParseException(u8"Option ‘" + option + u8"’ requires an argument")
    {
    }
  };

  class option_not_has_argument_exception : public OptionParseException
  {
    public:
    option_not_has_argument_exception
    (
      const std::string& option,
      const std::string& arg
    )
    : OptionParseException(
        u8"Option ‘" + option + u8"’ does not take an argument, but argument‘"
        + arg + "’ given")
    {
    }
  };

  class option_not_present_exception : public OptionParseException
  {
    public:
    option_not_present_exception(const std::string& option)
    : OptionParseException(u8"Option ‘" + option + u8"’ not present")
    {
    }
  };

  class OptionAdder;

  class OptionDetails
  {
    public:
    OptionDetails
    (
      const std::string& description,
      std::shared_ptr<const Value> value
    )
    : m_desc(description)
    , m_value(value)
    , m_count(0)
    {
    }

    const std::string&
    description() const
    {
      return m_desc;
    }

    bool
    has_arg() const
    {
      return m_value->has_arg();
    }

    void
    parse(const std::string& text)
    {
      m_value->parse(text);
      ++m_count;
    }

    int
    count() const
    {
      return m_count;
    }

    template <typename T>
    const T&
    as() const
    {
      return dynamic_cast<const values::default_value<T>&>(*m_value).get();
    }

    private:
    std::string m_desc;
    std::shared_ptr<const Value> m_value;
    int m_count;
  };

  struct HelpDetails
  {
    std::string s;
    std::string l;
    std::string desc;
    bool has_arg;
  };

  class Options
  {
    public:

    void
    parse(int& argc, char**& argv);

    OptionAdder
    add_options();

    void
    add_option
    (
      const std::string& s,
      const std::string& l,
      const std::string& desc,
      std::shared_ptr<const Value> value
    );

    int
    count(const std::string& o) const
    {
      auto iter = m_options.find(o);
      if (iter == m_options.end())
      {
        return 0;
      }

      return iter->second->count();
    }

    const OptionDetails&
    operator[](const std::string& option) const
    {
      auto iter = m_options.find(option);

      if (iter == m_options.end())
      {
        throw option_not_present_exception(option);
      }

      return *iter->second;
    }

    //parse positional arguments into the given option
    void
    parse_positional(std::string option);

    std::string
    help() const;

    private:

    void
    add_one_option
    (
      const std::string& option,
      std::shared_ptr<OptionDetails> details
    );

    bool
    consume_positional(std::string a);

    void
    add_to_option(const std::string& option, const std::string& arg);

    void
    parse_option
    (
      std::shared_ptr<OptionDetails> value,
      const std::string& name,
      const std::string& arg = ""
    );

    void
    checked_parse_arg
    (
      int argc,
      char* argv[],
      int argPos,
      std::shared_ptr<OptionDetails> value,
      const std::string& name
    );

    std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
    std::string m_positional;

    //mapping from groups to help options
    std::map<std::string, std::vector<HelpDetails>> m_help;
  };

  class OptionAdder
  {
    public:

    OptionAdder(Options& options)
    : m_options(options)
    {
    }

    OptionAdder&
    operator()
    (
      const std::string& opts,
      const std::string& desc,
      std::shared_ptr<const Value> value
        = ::cxxopts::value<bool>()
    );

    private:
    Options& m_options;
  };

}
<commit_msg>put in place check for value error<commit_after>/*

Copyright (c) 2014 Jarryd Beck

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

#include <regex>
#include <set>
#include <map>
#include <exception>
#include <sstream>

namespace cxxopts
{
  class Value
  {
    public:

    virtual void
    parse(const std::string& text) const = 0;

    virtual bool
    has_arg() const = 0;
  };

  namespace values
  {
    template <typename T>
    void
    parse_value(const std::string& text, T& value)
    {
      std::istringstream is(text);
      if (!(is >> value))
      {
      }

      if (!is.eof())
      {
      }
    }

    template <typename T>
    void
    parse_value(const std::string& text, std::vector<T>& value)
    {
      T v;
      parse_value(text, v);
      value.push_back(v);
    }

    template <typename T>
    struct value_has_arg
    {
      static constexpr bool value = true;
    };

    template <>
    struct value_has_arg<bool>
    {
      static constexpr bool value = false;
    };

    template <typename T>
    class default_value : public Value
    {
      public:
      default_value()
      : m_result(std::make_shared<T>())
      , m_store(m_result.get())
      {
      }

      default_value(T* t)
      : m_store(t)
      {
      }

      void
      parse(const std::string& text) const
      {
        parse_value(text, *m_store);
      }

      bool
      has_arg() const
      {
        return value_has_arg<T>::value;
      }

      const T&
      get() const
      {
        if (m_store == nullptr)
        {
          return *m_result;
        }
        else
        {
          return *m_store;
        }
      }

      private:
      std::shared_ptr<T> m_result;
      T* m_store;
    };

  }

  template <typename T>
  std::shared_ptr<Value>
  value()
  {
    return std::make_shared<values::default_value<T>>();
  }

  class OptionException : public std::exception
  {
    public:
    OptionException(const std::string& message)
    : m_message(message)
    {
    }

    virtual const char*
    what() const noexcept
    {
      return m_message.c_str();
    }

    private:
    std::string m_message;
  };

  class OptionSpecException : public OptionException
  {
    public:

    OptionSpecException(const std::string& message)
    : OptionException(message)
    {
    }
  };

  class OptionParseException : public OptionException
  {
    public:
    OptionParseException(const std::string& message)
    : OptionException(message)
    {
    }
  };

  class option_exists_error : public OptionSpecException
  {
    public:
    option_exists_error(const std::string& option)
    : OptionSpecException(u8"Option ‘" + option + u8"’ already exists")
    {
    }
  };

  class invalid_option_format_error : public OptionSpecException
  {
    public:
    invalid_option_format_error(const std::string& format)
    : OptionSpecException(u8"Invalid option format ‘" + format + u8"’")
    {
    }
  };

  class option_not_exists_exception : public OptionParseException
  {
    public:
    option_not_exists_exception(const std::string& option)
    : OptionParseException(u8"Option ‘" + option + u8"’ does not exist")
    {
    }
  };

  class missing_argument_exception : public OptionParseException
  {
    public:
    missing_argument_exception(const std::string& option)
    : OptionParseException(u8"Option ‘" + option + u8"’ is missing an argument")
    {
    }
  };

  class option_requires_argument_exception : public OptionParseException
  {
    public:
    option_requires_argument_exception(const std::string& option)
    : OptionParseException(u8"Option ‘" + option + u8"’ requires an argument")
    {
    }
  };

  class option_not_has_argument_exception : public OptionParseException
  {
    public:
    option_not_has_argument_exception
    (
      const std::string& option,
      const std::string& arg
    )
    : OptionParseException(
        u8"Option ‘" + option + u8"’ does not take an argument, but argument‘"
        + arg + "’ given")
    {
    }
  };

  class option_not_present_exception : public OptionParseException
  {
    public:
    option_not_present_exception(const std::string& option)
    : OptionParseException(u8"Option ‘" + option + u8"’ not present")
    {
    }
  };

  class OptionAdder;

  class OptionDetails
  {
    public:
    OptionDetails
    (
      const std::string& description,
      std::shared_ptr<const Value> value
    )
    : m_desc(description)
    , m_value(value)
    , m_count(0)
    {
    }

    const std::string&
    description() const
    {
      return m_desc;
    }

    bool
    has_arg() const
    {
      return m_value->has_arg();
    }

    void
    parse(const std::string& text)
    {
      m_value->parse(text);
      ++m_count;
    }

    int
    count() const
    {
      return m_count;
    }

    template <typename T>
    const T&
    as() const
    {
      return dynamic_cast<const values::default_value<T>&>(*m_value).get();
    }

    private:
    std::string m_desc;
    std::shared_ptr<const Value> m_value;
    int m_count;
  };

  struct HelpDetails
  {
    std::string s;
    std::string l;
    std::string desc;
    bool has_arg;
  };

  class Options
  {
    public:

    void
    parse(int& argc, char**& argv);

    OptionAdder
    add_options();

    void
    add_option
    (
      const std::string& s,
      const std::string& l,
      const std::string& desc,
      std::shared_ptr<const Value> value
    );

    int
    count(const std::string& o) const
    {
      auto iter = m_options.find(o);
      if (iter == m_options.end())
      {
        return 0;
      }

      return iter->second->count();
    }

    const OptionDetails&
    operator[](const std::string& option) const
    {
      auto iter = m_options.find(option);

      if (iter == m_options.end())
      {
        throw option_not_present_exception(option);
      }

      return *iter->second;
    }

    //parse positional arguments into the given option
    void
    parse_positional(std::string option);

    std::string
    help() const;

    private:

    void
    add_one_option
    (
      const std::string& option,
      std::shared_ptr<OptionDetails> details
    );

    bool
    consume_positional(std::string a);

    void
    add_to_option(const std::string& option, const std::string& arg);

    void
    parse_option
    (
      std::shared_ptr<OptionDetails> value,
      const std::string& name,
      const std::string& arg = ""
    );

    void
    checked_parse_arg
    (
      int argc,
      char* argv[],
      int argPos,
      std::shared_ptr<OptionDetails> value,
      const std::string& name
    );

    std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
    std::string m_positional;

    //mapping from groups to help options
    std::map<std::string, std::vector<HelpDetails>> m_help;
  };

  class OptionAdder
  {
    public:

    OptionAdder(Options& options)
    : m_options(options)
    {
    }

    OptionAdder&
    operator()
    (
      const std::string& opts,
      const std::string& desc,
      std::shared_ptr<const Value> value
        = ::cxxopts::value<bool>()
    );

    private:
    Options& m_options;
  };

}
<|endoftext|>


<commit_before>#include <tests/lib/test.h>

#include <tests/lib/glib-helpers/test-conn-helper.h>

#include <tests/lib/glib/simple-conn.h>
#include <tests/lib/glib/captcha-chan.h>

#include <TelepathyQt/Captcha>
#include <TelepathyQt/CaptchaAuthentication>
#include <TelepathyQt/Connection>
#include <TelepathyQt/PendingReady>
#include <TelepathyQt/PendingCaptchas>
#include <TelepathyQt/ReferencedHandles>

#include <telepathy-glib/telepathy-glib.h>

#include <stdio.h>

using namespace Tp;

class TestCaptchaAuthentication : public Test
{
    Q_OBJECT

public:
    TestCaptchaAuthentication(QObject *parent = 0)
        : Test(parent),
          mConn(0), mChanService(0)
    { }

protected Q_SLOTS:
    void onStatusChanged(Tp::CaptchaStatus status);

private Q_SLOTS:
    void initTestCase();
    void init();

    void testCreation();
    void testCaptchaSuccessful();
    void testCaptchaRetry();
    void testCaptchaCancel();
    void testNoCaptcha();

    void cleanup();
    void cleanupTestCase();

private:
    void createCaptchaChannel(bool canRetry = false);

    TestConnHelper *mConn;
    TpTestsCaptchaChannel *mChanService;
    ChannelPtr mChan;
    CaptchaAuthenticationPtr mCaptcha;
};

void TestCaptchaAuthentication::onStatusChanged(Tp::CaptchaStatus status)
{
    mLoop->exit(0);
}

void TestCaptchaAuthentication::createCaptchaChannel(bool canRetry)
{
    mChan.reset();
    mLoop->processEvents();
    tp_clear_object(&mChanService);

    /* Create service-side tube channel object */
    QString chanPath = QString(QLatin1String("%1/Channel")).arg(mConn->objectPath());

    mChanService = TP_TESTS_CAPTCHA_CHANNEL(g_object_new(
            TP_TESTS_TYPE_CAPTCHA_CHANNEL,
            "connection", mConn->service(),
            "requested", FALSE,
            "object-path", chanPath.toLatin1().constData(),
            "can-retry-captcha", canRetry,
            NULL));

    /* Create client-side tube channel object */
    mChan = Channel::create(mConn->client(), chanPath, QVariantMap());

    QVERIFY(connect(mChan->becomeReady(Channel::FeatureCaptcha),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(mChan->isReady(Channel::FeatureCaptcha), true);

    mCaptcha = mChan->captchaAuthentication();

    QCOMPARE(mCaptcha.isNull(), false);
}

void TestCaptchaAuthentication::initTestCase()
{
    initTestCaseImpl();

    g_type_init();
    g_set_prgname("captcha-authentication");
    tp_debug_set_flags("all");
    dbus_g_bus_get(DBUS_BUS_STARTER, 0);

    mConn = new TestConnHelper(this,
            TP_TESTS_TYPE_SIMPLE_CONNECTION,
            "account", "me@example.com",
            "protocol", "example",
            NULL);
    QCOMPARE(mConn->connect(), true);
}

void TestCaptchaAuthentication::init()
{
    initImpl();
}

void TestCaptchaAuthentication::testCreation()
{
    createCaptchaChannel();

    QCOMPARE(mCaptcha->status(), Tp::CaptchaStatusLocalPending);
    QCOMPARE(mCaptcha->canRetry(), false);
    QVERIFY(mCaptcha->lastError().isEmpty());
    QVERIFY(mCaptcha->lastErrorDetails().allDetails().isEmpty());
}

void TestCaptchaAuthentication::testCaptchaSuccessful()
{
    createCaptchaChannel();

    QSignalSpy spy(mCaptcha.data(), SIGNAL(statusChanged(Tp::CaptchaStatus)));

    PendingCaptchas *pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("image/png"));
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(pendingCaptchas->requiresMultipleCaptchas(), false);
    QCOMPARE(pendingCaptchas->captchaList().size(), 1);

    Captcha captchaData = pendingCaptchas->captcha();

    QCOMPARE(captchaData.mimeType(), QLatin1String("image/png"));
    QCOMPARE(captchaData.label(), QLatin1String("Enter the text displayed"));
    QCOMPARE(captchaData.data(), QByteArray("This is a fake payload"));
    QCOMPARE(captchaData.type(), CaptchaAuthentication::OCRChallenge);
    QCOMPARE((int)captchaData.id(), 42);

    QVERIFY(connect(mCaptcha->answer(42, QLatin1String("This is the right answer")),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(spy.size(), 2);
    QCOMPARE(mCaptcha->status(), CaptchaStatusSucceeded);
}

void TestCaptchaAuthentication::testCaptchaRetry()
{
    createCaptchaChannel(true);
    QSignalSpy spy(mCaptcha.data(), SIGNAL(statusChanged(Tp::CaptchaStatus)));

    PendingCaptchas *pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("image/png"));
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QVERIFY(connect(mCaptcha->answer(42, QLatin1String("What is this I don't even")),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(mCaptcha->status(), CaptchaStatusTryAgain);

    pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("image/png"));
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(pendingCaptchas->requiresMultipleCaptchas(), false);
    QCOMPARE(pendingCaptchas->captchaList().size(), 1);

    Captcha captchaData = pendingCaptchas->captcha();

    QCOMPARE(captchaData.mimeType(), QLatin1String("image/png"));
    QCOMPARE(captchaData.label(), QLatin1String("Enter the text displayed"));
    QCOMPARE(captchaData.data(), QByteArray("This is a reloaded payload"));
    QCOMPARE(captchaData.type(), CaptchaAuthentication::OCRChallenge);
    QCOMPARE((int)captchaData.id(), 42);

    QVERIFY(connect(mCaptcha->answer(42, QLatin1String("This is the right answer")),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(mCaptcha->status(), CaptchaStatusSucceeded);

    // Check signals now
    QCOMPARE(spy.size(), 5);
}

void TestCaptchaAuthentication::testCaptchaCancel()
{
    createCaptchaChannel();

    PendingCaptchas *pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("image/png"));
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    // Check that the result is not still available
    QCOMPARE(pendingCaptchas->captchaList().size(), 0);
    QCOMPARE((int)pendingCaptchas->captcha().id(), 0);
    QCOMPARE(mLoop->exec(), 0);

    // Cancel now
    QVERIFY(connect(mCaptcha->cancel(CaptchaCancelReasonUserCancelled),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(mCaptcha->status(), CaptchaStatusFailed);

    // Now try performing random actions which should fail
    QVERIFY(connect(mCaptcha->answer(42, QLatin1String("This is the right answer")),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QVERIFY(connect(mCaptcha->answer(CaptchaAnswers()),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QVERIFY(connect(mCaptcha->requestCaptchas(),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
}

void TestCaptchaAuthentication::testNoCaptcha()
{
    createCaptchaChannel();

    PendingCaptchas *pendingCaptchas = mCaptcha->requestCaptchas(QStringList(), CaptchaAuthentication::AudioRecognitionChallenge);
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("nosuchtype"),
        CaptchaAuthentication::SpeechRecognitionChallenge | CaptchaAuthentication::SpeechQuestionChallenge);
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    // Get the qa one
    pendingCaptchas = mCaptcha->requestCaptchas(QStringList(), CaptchaAuthentication::TextQuestionChallenge);
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    Captcha data = pendingCaptchas->captcha();
    Captcha data2(pendingCaptchas->captcha());

    QCOMPARE(data.id(), pendingCaptchas->captcha().id());
    QCOMPARE(data.id(), data2.id());

    // Get the video one to fail utterly
    pendingCaptchas = mCaptcha->requestCaptchas(QStringList(), CaptchaAuthentication::VideoRecognitionChallenge);
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
}

void TestCaptchaAuthentication::cleanup()
{
    cleanupImpl();

    if (mChan && mChan->isValid()) {
        qDebug() << "waiting for the channel to become invalidated";

        QVERIFY(connect(mChan.data(),
                SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
                mLoop,
                SLOT(quit())));
        tp_base_channel_close(TP_BASE_CHANNEL(mChanService));
        QCOMPARE(mLoop->exec(), 0);
    }

    mChan.reset();

    if (mChanService != 0) {
        g_object_unref(mChanService);
        mChanService = 0;
    }

    mLoop->processEvents();
}

void TestCaptchaAuthentication::cleanupTestCase()
{
    QCOMPARE(mConn->disconnect(), true);
    delete mConn;

    cleanupTestCaseImpl();
}

QTEST_MAIN(TestCaptchaAuthentication)
#include "_gen/captcha-authentication.cpp.moc.hpp"
<commit_msg>captcha-authentication: Cast the correct type<commit_after>#include <tests/lib/test.h>

#include <tests/lib/glib-helpers/test-conn-helper.h>

#include <tests/lib/glib/simple-conn.h>
#include <tests/lib/glib/captcha-chan.h>

#include <TelepathyQt/Captcha>
#include <TelepathyQt/CaptchaAuthentication>
#include <TelepathyQt/Connection>
#include <TelepathyQt/PendingReady>
#include <TelepathyQt/PendingCaptchas>
#include <TelepathyQt/ReferencedHandles>

#include <telepathy-glib/telepathy-glib.h>

#include <stdio.h>

using namespace Tp;

class TestCaptchaAuthentication : public Test
{
    Q_OBJECT

public:
    TestCaptchaAuthentication(QObject *parent = 0)
        : Test(parent),
          mConn(0), mChanService(0)
    { }

private Q_SLOTS:
    void initTestCase();
    void init();

    void testCreation();
    void testCaptchaSuccessful();
    void testCaptchaRetry();
    void testCaptchaCancel();
    void testNoCaptcha();

    void cleanup();
    void cleanupTestCase();

private:
    void createCaptchaChannel(bool canRetry = false);

    TestConnHelper *mConn;
    TpTestsCaptchaChannel *mChanService;
    ChannelPtr mChan;
    CaptchaAuthenticationPtr mCaptcha;
};

void TestCaptchaAuthentication::createCaptchaChannel(bool canRetry)
{
    mChan.reset();
    mLoop->processEvents();
    tp_clear_object(&mChanService);

    /* Create service-side tube channel object */
    QString chanPath = QString(QLatin1String("%1/Channel")).arg(mConn->objectPath());

    mChanService = TP_TESTS_CAPTCHA_CHANNEL(g_object_new(
            TP_TESTS_TYPE_CAPTCHA_CHANNEL,
            "connection", mConn->service(),
            "requested", FALSE,
            "object-path", chanPath.toLatin1().constData(),
            "can-retry-captcha", canRetry,
            NULL));

    /* Create client-side tube channel object */
    mChan = Channel::create(mConn->client(), chanPath, QVariantMap());

    QVERIFY(connect(mChan->becomeReady(Channel::FeatureCaptcha),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(mChan->isReady(Channel::FeatureCaptcha), true);

    mCaptcha = mChan->captchaAuthentication();

    QCOMPARE(mCaptcha.isNull(), false);
}

void TestCaptchaAuthentication::initTestCase()
{
    initTestCaseImpl();

    g_type_init();
    g_set_prgname("captcha-authentication");
    tp_debug_set_flags("all");
    dbus_g_bus_get(DBUS_BUS_STARTER, 0);

    mConn = new TestConnHelper(this,
            TP_TESTS_TYPE_SIMPLE_CONNECTION,
            "account", "me@example.com",
            "protocol", "example",
            NULL);
    QCOMPARE(mConn->connect(), true);
}

void TestCaptchaAuthentication::init()
{
    initImpl();
}

void TestCaptchaAuthentication::testCreation()
{
    createCaptchaChannel();

    QCOMPARE(mCaptcha->status(), Tp::CaptchaStatusLocalPending);
    QCOMPARE(mCaptcha->canRetry(), false);
    QVERIFY(mCaptcha->lastError().isEmpty());
    QVERIFY(mCaptcha->lastErrorDetails().allDetails().isEmpty());
}

void TestCaptchaAuthentication::testCaptchaSuccessful()
{
    createCaptchaChannel();

    QSignalSpy spy(mCaptcha.data(), SIGNAL(statusChanged(Tp::CaptchaStatus)));

    PendingCaptchas *pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("image/png"));
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(pendingCaptchas->requiresMultipleCaptchas(), false);
    QCOMPARE(pendingCaptchas->captchaList().size(), 1);

    Captcha captchaData = pendingCaptchas->captcha();

    QCOMPARE(captchaData.mimeType(), QLatin1String("image/png"));
    QCOMPARE(captchaData.label(), QLatin1String("Enter the text displayed"));
    QCOMPARE(captchaData.data(), QByteArray("This is a fake payload"));
    QCOMPARE(captchaData.type(), CaptchaAuthentication::OCRChallenge);
    QCOMPARE(captchaData.id(), (uint)42);

    QVERIFY(connect(mCaptcha->answer(42, QLatin1String("This is the right answer")),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(spy.size(), 2);
    QCOMPARE(mCaptcha->status(), CaptchaStatusSucceeded);
}

void TestCaptchaAuthentication::testCaptchaRetry()
{
    createCaptchaChannel(true);
    QSignalSpy spy(mCaptcha.data(), SIGNAL(statusChanged(Tp::CaptchaStatus)));

    PendingCaptchas *pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("image/png"));
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QVERIFY(connect(mCaptcha->answer(42, QLatin1String("What is this I don't even")),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(mCaptcha->status(), CaptchaStatusTryAgain);

    pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("image/png"));
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(pendingCaptchas->requiresMultipleCaptchas(), false);
    QCOMPARE(pendingCaptchas->captchaList().size(), 1);

    Captcha captchaData = pendingCaptchas->captcha();

    QCOMPARE(captchaData.mimeType(), QLatin1String("image/png"));
    QCOMPARE(captchaData.label(), QLatin1String("Enter the text displayed"));
    QCOMPARE(captchaData.data(), QByteArray("This is a reloaded payload"));
    QCOMPARE(captchaData.type(), CaptchaAuthentication::OCRChallenge);
    QCOMPARE(captchaData.id(), (uint)42);

    QVERIFY(connect(mCaptcha->answer(42, QLatin1String("This is the right answer")),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(mCaptcha->status(), CaptchaStatusSucceeded);

    // Check signals now
    QCOMPARE(spy.size(), 5);
}

void TestCaptchaAuthentication::testCaptchaCancel()
{
    createCaptchaChannel();

    PendingCaptchas *pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("image/png"));
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    // Check that the result is not still available
    QCOMPARE(pendingCaptchas->captchaList().size(), 0);
    QCOMPARE(pendingCaptchas->captcha().id(), (uint)0);
    QCOMPARE(mLoop->exec(), 0);

    // Cancel now
    QVERIFY(connect(mCaptcha->cancel(CaptchaCancelReasonUserCancelled),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QCOMPARE(mCaptcha->status(), CaptchaStatusFailed);

    // Now try performing random actions which should fail
    QVERIFY(connect(mCaptcha->answer(42, QLatin1String("This is the right answer")),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QVERIFY(connect(mCaptcha->answer(CaptchaAnswers()),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    QVERIFY(connect(mCaptcha->requestCaptchas(),
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
}

void TestCaptchaAuthentication::testNoCaptcha()
{
    createCaptchaChannel();

    PendingCaptchas *pendingCaptchas = mCaptcha->requestCaptchas(QStringList(), CaptchaAuthentication::AudioRecognitionChallenge);
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    pendingCaptchas = mCaptcha->requestCaptchas(QStringList() << QLatin1String("nosuchtype"),
        CaptchaAuthentication::SpeechRecognitionChallenge | CaptchaAuthentication::SpeechQuestionChallenge);
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    // Get the qa one
    pendingCaptchas = mCaptcha->requestCaptchas(QStringList(), CaptchaAuthentication::TextQuestionChallenge);
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);

    Captcha data = pendingCaptchas->captcha();
    Captcha data2(pendingCaptchas->captcha());

    QCOMPARE(data.id(), pendingCaptchas->captcha().id());
    QCOMPARE(data.id(), data2.id());

    // Get the video one to fail utterly
    pendingCaptchas = mCaptcha->requestCaptchas(QStringList(), CaptchaAuthentication::VideoRecognitionChallenge);
    QVERIFY(connect(pendingCaptchas,
                SIGNAL(finished(Tp::PendingOperation *)),
                SLOT(expectFailure(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
}

void TestCaptchaAuthentication::cleanup()
{
    cleanupImpl();

    if (mChan && mChan->isValid()) {
        qDebug() << "waiting for the channel to become invalidated";

        QVERIFY(connect(mChan.data(),
                SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
                mLoop,
                SLOT(quit())));
        tp_base_channel_close(TP_BASE_CHANNEL(mChanService));
        QCOMPARE(mLoop->exec(), 0);
    }

    mChan.reset();

    if (mChanService != 0) {
        g_object_unref(mChanService);
        mChanService = 0;
    }

    mLoop->processEvents();
}

void TestCaptchaAuthentication::cleanupTestCase()
{
    QCOMPARE(mConn->disconnect(), true);
    delete mConn;

    cleanupTestCaseImpl();
}

QTEST_MAIN(TestCaptchaAuthentication)
#include "_gen/captcha-authentication.cpp.moc.hpp"
<|endoftext|>


<commit_before>// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0

#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;

#include <jsonipc/jsonipc.hh>

#include <bse/bseenums.hh>      // enums API interfaces, etc
#include <bse/platform.hh>
#include <bse/regex.hh>
#include <bse/bse.hh>   // Bse::init_async
#include <limits.h>
#include <stdlib.h>

#include "bsebus.hh"
#include "bsecontextmerger.hh"
#include "bsecsynth.hh"
#include "bseeditablesample.hh"
#include "bsemidinotifier.hh"
#include "bsemidisynth.hh"
#include "bsepart.hh"
#include "bsepcmwriter.hh"
#include "bseproject.hh"
#include "bseserver.hh"
#include "bsesnet.hh"
#include "bsesong.hh"
#include "bsesong.hh"
#include "bsesoundfont.hh"
#include "bsesoundfontrepo.hh"
#include "bsetrack.hh"
#include "bsewave.hh"
#include "bsewaveosc.hh"
#include "bsewaverepo.hh"
#include "monitor.hh"
#include "bse/bseapi_jsonipc.cc"    // Bse_jsonipc_stub

#undef B0 // pollution from termios.h

static Bse::ServerH bse_server;
static Jsonipc::IpcDispatcher *dispatcher = NULL;
static bool verbose = false;

// Configure websocket server
struct CustomServerConfig : public websocketpp::config::asio {
  static const size_t connection_read_buffer_size = 16384;
};
using ServerEndpoint = websocketpp::server<CustomServerConfig>;
static ServerEndpoint websocket_server;

static std::string
handle_jsonipc (const std::string &message, const websocketpp::connection_hdl &hdl)
{
  const ptrdiff_t conid = ptrdiff_t (websocket_server.get_con_from_hdl (hdl).get());
  if (verbose)
    Bse::printerr ("%p: REQUEST: %s\n", conid, message);
  const std::string reply = dispatcher->dispatch_message (message);
  if (verbose)
    Bse::printerr ("%p: REPLY:   %s\n", conid, reply);
  return reply;
}

static std::string
user_agent_nick (const std::string &useragent)
{
  using namespace Bse;
  std::string nick;
  if      (Re::search (R"(\bFirefox/)", useragent))
    nick += "Firefox";
  else if (Re::search (R"(\bElectron/)", useragent))
    nick += "Electron";
  else if (Re::search (R"(\bChrome/)", useragent))
    nick += "Chrome";
  else if (Re::search (R"(\bSafari/)", useragent))
    nick += "Safari";
  else
    nick += "Unknown";
  return nick;
}

static bool
ws_validate_connection (websocketpp::connection_hdl hdl)
{
  ServerEndpoint::connection_ptr con = websocket_server.get_con_from_hdl (hdl);
  // using subprotocol as auth string
  const std::vector<std::string> &subprotocols = con->get_requested_subprotocols();
  if (subprotocols.size() == 1)
    {
      if (subprotocols[0] == "auth123")
        {
          con->select_subprotocol (subprotocols[0]);
          return true;
        }
    }
  return false;
}

static void
ws_open_connection (websocketpp::connection_hdl hdl)
{
  ServerEndpoint::connection_ptr con = websocket_server.get_con_from_hdl (hdl);
  // https://github.com/zaphoyd/websocketpp/issues/694#issuecomment-454623641
  const auto &socket = con->get_raw_socket();
  const auto &address = socket.remote_endpoint().address();
  const int rport = socket.remote_endpoint().port();
  const websocketpp::http::parser::request &rq = con->get_request();
  const websocketpp::http::parser::header_list &headermap = rq.get_headers();
  std::string useragent;
  for (auto it : headermap) // request headers
    if (it.first == "User-Agent")
      useragent = it.second;
  std::string nick = user_agent_nick (useragent);
  if (!nick.empty())
    nick = "(" + nick + ")";
  using namespace Bse::AnsiColors;
  auto B1 = color (BOLD);
  auto B0 = color (BOLD_OFF);
  Bse::printout ("%p: %sACCEPT:%s %s:%d/ %s\n", ptrdiff_t (con.get()), B1, B0, address.to_string().c_str(), rport, nick);
  // Bse::printout ("User-Agent: %s\n", useragent);
}

static void
ws_message (websocketpp::connection_hdl con, server::message_ptr msg)
{
  const std::string &message = msg->get_payload();
  // send message to BSE thread and block until its been handled
  Aida::ScopedSemaphore sem;
  auto handle_wsmsg = [&message, &con, &sem] () {
    std::string reply = handle_jsonipc (message, con);
    if (!reply.empty())
      websocket_server.send (con, reply, websocketpp::frame::opcode::text);
    sem.post();
  };
  bse_server.__iface_ptr__()->__execution_context_mt__().enqueue_mt (handle_wsmsg);
  sem.wait();
}

static std::string
simplify_path (const std::string &path)
{
  using namespace Bse;
  std::vector<std::string> dirs = Bse::string_split (path, "/");
  for (ssize_t i = 0; i < dirs.size(); i++)
    if (dirs[i].empty() || dirs[i] == ".")
      dirs.erase (dirs.begin() + i--);
    else if (dirs[i] == "..")
      {
        dirs.erase (dirs.begin() + i--);
        if (i >= 0)
          dirs.erase (dirs.begin() + i--);
      }
  return "/" + string_join ("/", dirs);
}

static std::string
app_path (const std::string &path)
{
  using namespace Bse;
  static const char *basepath = realpath ((runpath (RPath::INSTALLDIR) + "/app/").c_str(), NULL);
  if (basepath)
    {
      static size_t baselen = strlen (basepath);
      char *uripath = realpath ((basepath + std::string ("/") + path).c_str(), NULL);
      if (uripath && strncmp (uripath, basepath, baselen) == 0 && uripath[baselen] == '/')
        {
          std::string dest = uripath;
          free (uripath);
          return dest;
        }
    }
  return ""; // 404
}

static void
http_request (websocketpp::connection_hdl hdl)
{
  using namespace Bse;
  ServerEndpoint::connection_ptr con = websocket_server.get_con_from_hdl (hdl);
  const ptrdiff_t conid = ptrdiff_t (con.get());
  const auto &parts = Bse::string_split (con->get_resource(), "?");
  std::string simplepath = simplify_path (parts[0]);
  // root
  if (simplepath == "/")
    {
      con->append_header ("Content-Type", "text/html; charset=utf-8");
      con->set_body ("<!DOCTYPE html>\n"
                     "<title>Beast-Sound-Engine</title>\n"
                     "
Beast-Sound-Engine
\n"
                     "app.html
\n"
                     "Resource: " + con->get_resource() + "
\n"
                     "URI: " + con->get_uri()->str() + "
\n"
                     "\n");
      con->set_status (websocketpp::http::status_code::ok);
      if (verbose)
        Bse::printerr ("%p: GET:     %s\n", conid, simplepath);
      return;
    }
  // file
  std::string filepath = app_path (simplepath);
  if (Path::check (filepath, "drx"))
    filepath = Path::join (filepath, "index.html");
  if (Path::check (filepath, "fr"))
    {
      if (string_endswith (filepath, ".html"))
        con->append_header ("Content-Type", "text/html; charset=utf-8");
      else if (string_endswith (filepath, ".js") || string_endswith (filepath, ".mjs"))
        con->append_header ("Content-Type", "application/javascript");
      else if (string_endswith (filepath, ".css"))
        con->append_header ("Content-Type", "text/css");
      else
        con->append_header ("Content-Type", "application/octet-stream");
      Blob blob = Blob::from_file (filepath);
      con->set_body (blob.string());
      con->set_status (websocketpp::http::status_code::ok);
      if (verbose)
        Bse::printerr ("%p: FILE:    %s\n", conid, simplepath);
      return;
    }
  // 404
  con->append_header ("Content-Type", "text/html; charset=utf-8");
  con->set_status (websocketpp::http::status_code::not_found);
  con->set_body ("<!DOCTYPE html>\n"
                 "<title>404 Not Found</title>\n"
                 "
Not Found
\n"
                 "
The requested URL was not found: <tt>" + con->get_uri()->str() + "</tt>
\n"
                 "
<address>" "beast-sound-engine/" + Bse::version() + "</address>\n"
                 "
\n");
  if (verbose)
    Bse::printerr ("%p: 404:     %s\n", conid, simplepath);
}

static void
print_usage (bool help)
{
  if (!help)
    {
      Bse::printout ("beast-sound-engine version %s\n", Bse::version());
      return;
    }
  Bse::printout ("Usage: beast-sound-engine [OPTIONS]\n");
  Bse::printout ("  --help     Print command line help\n");
  Bse::printout ("  --version  Print program version\n");
  Bse::printout ("  --verbose  Print requests and replies\n");
}

int
main (int argc, char *argv[])
{
  Bse::init_async (&argc, argv, argv[0]); // Bse::cstrings_to_vector (NULL)
  bse_server = Bse::init_server_instance();

  // Register BSE bindings
  const bool print_jsbse = argc >= 2 && std::string ("--js-bseapi") == argv[1];
  {
    Aida::ScopedSemaphore sem;
    auto handle_wsmsg = [print_jsbse,&sem] () {
      if (!print_jsbse)
        Jsonipc::ClassPrinter::disable();
      Bse_jsonipc_stub();
      if (print_jsbse)
        Bse::printout ("%s\n", Jsonipc::ClassPrinter::string());
      // fixups, we know Bse::Server is a singleton
      Jsonipc::Class<Bse::ServerIface> jsonipc__Bse_ServerIface;
      jsonipc__Bse_ServerIface.eternal();
      Jsonipc::Class<Bse::ServerImpl> jsonipc__Bse_ServerImpl;
      jsonipc__Bse_ServerImpl.eternal();
      sem.post();
    };
    bse_server.__iface_ptr__()->__execution_context_mt__().enqueue_mt (handle_wsmsg);
    sem.wait();
    if (print_jsbse)
      return 0;
  }

  // Setup Jsonipc dispatcher
  dispatcher = new Jsonipc::IpcDispatcher();
  dispatcher->add_method ("$jsonipc.initialize",
                          [] (Jsonipc::JsonCallbackInfo &cbi) -> std::string* {
                            // TODO: bind Bse::ServerImpl, so far we only bind Bse::ServerIface
                            Bse::ServerIface &server_iface = Bse::ServerImpl::instance();
                            cbi.set_result (Jsonipc::to_json (server_iface, cbi.allocator()).Move());
                            return NULL;
                          });

  // parse arguments
  bool seen_dashdash = false;
  std::vector<std::string> words;
  for (size_t i = 0; i < argc; i++)
    if (!argv[i])
      continue;
    else if (argv[i][0] == '-' && argv[i][1] == '-' && argv[i][2] == 0)
      seen_dashdash = true;
    else if (!seen_dashdash && argv[i][0] == '-')
      {
        const char *arg = argv[i] + 1 + (argv[i][1] == '-');
        const char *eq = strchr (arg, '=');
        const std::string arg_name = !eq ? arg : std::string (arg, eq - arg);
        if (arg_name == "version")
          {
            print_usage (false);
            return 0;
          }
        else if (arg_name == "h" || arg_name == "help")
          {
            print_usage (true);
            return 0;
          }
        else if (arg_name == "v" || arg_name == "verbose")
          {
            verbose = true;
          }
        else
          {
            Bse::printerr ("%s: invalid argument: %s\n", argv[0], argv[i]);
            print_usage (true);
            return 1;
          }
      }
    else
      words.push_back (argv[i]);

  const int BEAST_AUDIO_ENGINE_PORT = 27239;    // 0x3ea67 % 32768

  // setup websocket and run asio loop
  websocket_server.set_user_agent ("beast-sound-engine/" + Bse::version());
  websocket_server.set_http_handler (&http_request);
  websocket_server.set_validate_handler (&ws_validate_connection);
  websocket_server.set_open_handler (&ws_open_connection);
  websocket_server.set_message_handler (&ws_message);
  websocket_server.init_asio();
  websocket_server.clear_access_channels (websocketpp::log::alevel::all);
  websocket_server.set_reuse_addr (true);
  namespace IP = boost::asio::ip;
  IP::tcp::endpoint endpoint_local = IP::tcp::endpoint (IP::address::from_string ("127.0.0.1"), BEAST_AUDIO_ENGINE_PORT);
  websocket_server.listen (endpoint_local);
  websocket_server.start_accept();

  using namespace Bse::AnsiColors;
  auto B1 = color (BOLD);
  auto B0 = color (BOLD_OFF);
  Bse::printout ("%sLISTEN:%s http://localhost:%d/app.html\n", B1, B0, BEAST_AUDIO_ENGINE_PORT);

  websocket_server.run();

  return 0;
}

/* Dumb echo test:
   var WebSocket = require ('ws'), c = 0; ws = new WebSocket("ws://localhost:27239/", 'auth123'); ws.onopen=e=>ws.send("Hello!");
   ws.onmessage=e=>{if(++c % 1000 == 0) console.log(e.data, c); ws.send("YO"); }; setTimeout(e=>{ws.close();console.log(c/10);},10000);
 */
<commit_msg>BSE: beast-sound-engine: use Jsonipc::ClassPrinter::to_string()<commit_after>// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0

#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;

#include <jsonipc/jsonipc.hh>

#include <bse/bseenums.hh>      // enums API interfaces, etc
#include <bse/platform.hh>
#include <bse/regex.hh>
#include <bse/bse.hh>   // Bse::init_async
#include <limits.h>
#include <stdlib.h>

#include "bsebus.hh"
#include "bsecontextmerger.hh"
#include "bsecsynth.hh"
#include "bseeditablesample.hh"
#include "bsemidinotifier.hh"
#include "bsemidisynth.hh"
#include "bsepart.hh"
#include "bsepcmwriter.hh"
#include "bseproject.hh"
#include "bseserver.hh"
#include "bsesnet.hh"
#include "bsesong.hh"
#include "bsesong.hh"
#include "bsesoundfont.hh"
#include "bsesoundfontrepo.hh"
#include "bsetrack.hh"
#include "bsewave.hh"
#include "bsewaveosc.hh"
#include "bsewaverepo.hh"
#include "monitor.hh"
#include "bse/bseapi_jsonipc.cc"    // Bse_jsonipc_stub

#undef B0 // pollution from termios.h

static Bse::ServerH bse_server;
static Jsonipc::IpcDispatcher *dispatcher = NULL;
static bool verbose = false;

// Configure websocket server
struct CustomServerConfig : public websocketpp::config::asio {
  static const size_t connection_read_buffer_size = 16384;
};
using ServerEndpoint = websocketpp::server<CustomServerConfig>;
static ServerEndpoint websocket_server;

static std::string
handle_jsonipc (const std::string &message, const websocketpp::connection_hdl &hdl)
{
  const ptrdiff_t conid = ptrdiff_t (websocket_server.get_con_from_hdl (hdl).get());
  if (verbose)
    Bse::printerr ("%p: REQUEST: %s\n", conid, message);
  const std::string reply = dispatcher->dispatch_message (message);
  if (verbose)
    Bse::printerr ("%p: REPLY:   %s\n", conid, reply);
  return reply;
}

static std::string
user_agent_nick (const std::string &useragent)
{
  using namespace Bse;
  std::string nick;
  if      (Re::search (R"(\bFirefox/)", useragent))
    nick += "Firefox";
  else if (Re::search (R"(\bElectron/)", useragent))
    nick += "Electron";
  else if (Re::search (R"(\bChrome/)", useragent))
    nick += "Chrome";
  else if (Re::search (R"(\bSafari/)", useragent))
    nick += "Safari";
  else
    nick += "Unknown";
  return nick;
}

static bool
ws_validate_connection (websocketpp::connection_hdl hdl)
{
  ServerEndpoint::connection_ptr con = websocket_server.get_con_from_hdl (hdl);
  // using subprotocol as auth string
  const std::vector<std::string> &subprotocols = con->get_requested_subprotocols();
  if (subprotocols.size() == 1)
    {
      if (subprotocols[0] == "auth123")
        {
          con->select_subprotocol (subprotocols[0]);
          return true;
        }
    }
  return false;
}

static void
ws_open_connection (websocketpp::connection_hdl hdl)
{
  ServerEndpoint::connection_ptr con = websocket_server.get_con_from_hdl (hdl);
  // https://github.com/zaphoyd/websocketpp/issues/694#issuecomment-454623641
  const auto &socket = con->get_raw_socket();
  const auto &address = socket.remote_endpoint().address();
  const int rport = socket.remote_endpoint().port();
  const websocketpp::http::parser::request &rq = con->get_request();
  const websocketpp::http::parser::header_list &headermap = rq.get_headers();
  std::string useragent;
  for (auto it : headermap) // request headers
    if (it.first == "User-Agent")
      useragent = it.second;
  std::string nick = user_agent_nick (useragent);
  if (!nick.empty())
    nick = "(" + nick + ")";
  using namespace Bse::AnsiColors;
  auto B1 = color (BOLD);
  auto B0 = color (BOLD_OFF);
  Bse::printout ("%p: %sACCEPT:%s %s:%d/ %s\n", ptrdiff_t (con.get()), B1, B0, address.to_string().c_str(), rport, nick);
  // Bse::printout ("User-Agent: %s\n", useragent);
}

static void
ws_message (websocketpp::connection_hdl con, server::message_ptr msg)
{
  const std::string &message = msg->get_payload();
  // send message to BSE thread and block until its been handled
  Aida::ScopedSemaphore sem;
  auto handle_wsmsg = [&message, &con, &sem] () {
    std::string reply = handle_jsonipc (message, con);
    if (!reply.empty())
      websocket_server.send (con, reply, websocketpp::frame::opcode::text);
    sem.post();
  };
  bse_server.__iface_ptr__()->__execution_context_mt__().enqueue_mt (handle_wsmsg);
  sem.wait();
}

static std::string
simplify_path (const std::string &path)
{
  using namespace Bse;
  std::vector<std::string> dirs = Bse::string_split (path, "/");
  for (ssize_t i = 0; i < dirs.size(); i++)
    if (dirs[i].empty() || dirs[i] == ".")
      dirs.erase (dirs.begin() + i--);
    else if (dirs[i] == "..")
      {
        dirs.erase (dirs.begin() + i--);
        if (i >= 0)
          dirs.erase (dirs.begin() + i--);
      }
  return "/" + string_join ("/", dirs);
}

static std::string
app_path (const std::string &path)
{
  using namespace Bse;
  static const char *basepath = realpath ((runpath (RPath::INSTALLDIR) + "/app/").c_str(), NULL);
  if (basepath)
    {
      static size_t baselen = strlen (basepath);
      char *uripath = realpath ((basepath + std::string ("/") + path).c_str(), NULL);
      if (uripath && strncmp (uripath, basepath, baselen) == 0 && uripath[baselen] == '/')
        {
          std::string dest = uripath;
          free (uripath);
          return dest;
        }
    }
  return ""; // 404
}

static void
http_request (websocketpp::connection_hdl hdl)
{
  using namespace Bse;
  ServerEndpoint::connection_ptr con = websocket_server.get_con_from_hdl (hdl);
  const ptrdiff_t conid = ptrdiff_t (con.get());
  const auto &parts = Bse::string_split (con->get_resource(), "?");
  std::string simplepath = simplify_path (parts[0]);
  // root
  if (simplepath == "/")
    {
      con->append_header ("Content-Type", "text/html; charset=utf-8");
      con->set_body ("<!DOCTYPE html>\n"
                     "<title>Beast-Sound-Engine</title>\n"
                     "
Beast-Sound-Engine
\n"
                     "app.html
\n"
                     "Resource: " + con->get_resource() + "
\n"
                     "URI: " + con->get_uri()->str() + "
\n"
                     "\n");
      con->set_status (websocketpp::http::status_code::ok);
      if (verbose)
        Bse::printerr ("%p: GET:     %s\n", conid, simplepath);
      return;
    }
  // file
  std::string filepath = app_path (simplepath);
  if (Path::check (filepath, "drx"))
    filepath = Path::join (filepath, "index.html");
  if (Path::check (filepath, "fr"))
    {
      if (string_endswith (filepath, ".html"))
        con->append_header ("Content-Type", "text/html; charset=utf-8");
      else if (string_endswith (filepath, ".js") || string_endswith (filepath, ".mjs"))
        con->append_header ("Content-Type", "application/javascript");
      else if (string_endswith (filepath, ".css"))
        con->append_header ("Content-Type", "text/css");
      else
        con->append_header ("Content-Type", "application/octet-stream");
      Blob blob = Blob::from_file (filepath);
      con->set_body (blob.string());
      con->set_status (websocketpp::http::status_code::ok);
      if (verbose)
        Bse::printerr ("%p: FILE:    %s\n", conid, simplepath);
      return;
    }
  // 404
  con->append_header ("Content-Type", "text/html; charset=utf-8");
  con->set_status (websocketpp::http::status_code::not_found);
  con->set_body ("<!DOCTYPE html>\n"
                 "<title>404 Not Found</title>\n"
                 "
Not Found
\n"
                 "
The requested URL was not found: <tt>" + con->get_uri()->str() + "</tt>
\n"
                 "
<address>" "beast-sound-engine/" + Bse::version() + "</address>\n"
                 "
\n");
  if (verbose)
    Bse::printerr ("%p: 404:     %s\n", conid, simplepath);
}

static void
print_usage (bool help)
{
  if (!help)
    {
      Bse::printout ("beast-sound-engine version %s\n", Bse::version());
      return;
    }
  Bse::printout ("Usage: beast-sound-engine [OPTIONS]\n");
  Bse::printout ("  --help     Print command line help\n");
  Bse::printout ("  --version  Print program version\n");
  Bse::printout ("  --verbose  Print requests and replies\n");
}

int
main (int argc, char *argv[])
{
  Bse::init_async (&argc, argv, argv[0]); // Bse::cstrings_to_vector (NULL)
  bse_server = Bse::init_server_instance();

  // Register BSE bindings
  const bool print_jsbse = argc >= 2 && std::string ("--js-bseapi") == argv[1];
  {
    Aida::ScopedSemaphore sem;
    auto handle_wsmsg = [print_jsbse,&sem] () {
      if (!print_jsbse)
        Jsonipc::ClassPrinter::disable();
      Bse_jsonipc_stub();
      if (print_jsbse)
        Bse::printout ("%s\n", Jsonipc::ClassPrinter::to_string());
      // fixups, we know Bse::Server is a singleton
      Jsonipc::Class<Bse::ServerIface> jsonipc__Bse_ServerIface;
      jsonipc__Bse_ServerIface.eternal();
      Jsonipc::Class<Bse::ServerImpl> jsonipc__Bse_ServerImpl;
      jsonipc__Bse_ServerImpl.eternal();
      sem.post();
    };
    bse_server.__iface_ptr__()->__execution_context_mt__().enqueue_mt (handle_wsmsg);
    sem.wait();
    if (print_jsbse)
      return 0;
  }

  // Setup Jsonipc dispatcher
  dispatcher = new Jsonipc::IpcDispatcher();
  dispatcher->add_method ("$jsonipc.initialize",
                          [] (Jsonipc::JsonCallbackInfo &cbi) -> std::string* {
                            // TODO: bind Bse::ServerImpl, so far we only bind Bse::ServerIface
                            Bse::ServerIface &server_iface = Bse::ServerImpl::instance();
                            cbi.set_result (Jsonipc::to_json (server_iface, cbi.allocator()).Move());
                            return NULL;
                          });

  // parse arguments
  bool seen_dashdash = false;
  std::vector<std::string> words;
  for (size_t i = 0; i < argc; i++)
    if (!argv[i])
      continue;
    else if (argv[i][0] == '-' && argv[i][1] == '-' && argv[i][2] == 0)
      seen_dashdash = true;
    else if (!seen_dashdash && argv[i][0] == '-')
      {
        const char *arg = argv[i] + 1 + (argv[i][1] == '-');
        const char *eq = strchr (arg, '=');
        const std::string arg_name = !eq ? arg : std::string (arg, eq - arg);
        if (arg_name == "version")
          {
            print_usage (false);
            return 0;
          }
        else if (arg_name == "h" || arg_name == "help")
          {
            print_usage (true);
            return 0;
          }
        else if (arg_name == "v" || arg_name == "verbose")
          {
            verbose = true;
          }
        else
          {
            Bse::printerr ("%s: invalid argument: %s\n", argv[0], argv[i]);
            print_usage (true);
            return 1;
          }
      }
    else
      words.push_back (argv[i]);

  const int BEAST_AUDIO_ENGINE_PORT = 27239;    // 0x3ea67 % 32768

  // setup websocket and run asio loop
  websocket_server.set_user_agent ("beast-sound-engine/" + Bse::version());
  websocket_server.set_http_handler (&http_request);
  websocket_server.set_validate_handler (&ws_validate_connection);
  websocket_server.set_open_handler (&ws_open_connection);
  websocket_server.set_message_handler (&ws_message);
  websocket_server.init_asio();
  websocket_server.clear_access_channels (websocketpp::log::alevel::all);
  websocket_server.set_reuse_addr (true);
  namespace IP = boost::asio::ip;
  IP::tcp::endpoint endpoint_local = IP::tcp::endpoint (IP::address::from_string ("127.0.0.1"), BEAST_AUDIO_ENGINE_PORT);
  websocket_server.listen (endpoint_local);
  websocket_server.start_accept();

  using namespace Bse::AnsiColors;
  auto B1 = color (BOLD);
  auto B0 = color (BOLD_OFF);
  Bse::printout ("%sLISTEN:%s http://localhost:%d/app.html\n", B1, B0, BEAST_AUDIO_ENGINE_PORT);

  websocket_server.run();

  return 0;
}

/* Dumb echo test:
   var WebSocket = require ('ws'), c = 0; ws = new WebSocket("ws://localhost:27239/", 'auth123'); ws.onopen=e=>ws.send("Hello!");
   ws.onmessage=e=>{if(++c % 1000 == 0) console.log(e.data, c); ws.send("YO"); }; setTimeout(e=>{ws.close();console.log(c/10);},10000);
 */
<|endoftext|>