<commit_before>#include "apf.h"
#include "apfMDS.h"
#include "apfMesh2.h"
#include "pcu_io.h"
#include "pcu_byteorder.h"

#include <cstdio>
#include <cstring>
#include <cassert>
#include <cstdlib>

/*
read files in the AFLR3 format from Dave Marcum at Mississippi State
-little-endian if file has suffix '.lb8.ugrid'
-big-endian if file has suffix '.b8.ugrid'
-integers are 4B
-floats are 8B
*/

namespace {
  int faceTypeIdx(int type) {
    switch(type) {
      case apf::Mesh::TRIANGLE: return 0;
      case apf::Mesh::QUAD: return 1;
      default: {
                 assert(false);
                 return -1;
               }
    }
  }

  struct Reader {
    apf::Mesh2* mesh;
    FILE* file;
    std::map<long, apf::MeshEntity*> nodeMap;
    unsigned* faceVerts[2];
    unsigned* faceTags[2];
    bool swapBytes;
  };

  struct header {
    unsigned nvtx, ntri, nquad, ntet, npyr, nprz, nhex;
    void print() {
      fprintf(stderr,
          "nvtx %u ntri %u nquad %u ntet %u npyr %u nprz %u nhex %u\n",
          nvtx,ntri,nquad,ntet,npyr,nprz,nhex);
    }
  };

  void initReader(Reader* r, apf::Mesh2* m, const char* filename) {
    r->mesh = m;
    r->file = fopen(filename, "rb");
    if (!r->file) {
      fprintf(stderr,"ERROR couldn't open ugrid file \"%s\"\n",filename);
      abort();
    }
    unsigned endian = -1;
    if ( strstr(filename, ".b8.ugrid") ) {
      endian = PCU_BIG_ENDIAN;
    } else if ( strstr(filename, ".lb8.ugrid") ) {
      endian = PCU_LITTLE_ENDIAN;
    } else {
      fprintf(stderr,
          "ERROR file extension of \"%s\" is not supported\n", filename);
      exit(EXIT_FAILURE);
    }
    r->swapBytes = ( endian != PCU_HOST_ORDER );
  }

  void readUnsigneds(FILE* f, unsigned* v, size_t cnt, bool swap) {
    assert(sizeof(unsigned)==4);
    size_t read = fread(v, sizeof(unsigned), cnt, f);
    assert(read == cnt);
    if ( swap )
      pcu_swap_unsigneds(v,cnt);
  }

  void readDoubles(FILE* f, double* vals, size_t cnt, bool swap) {
    assert(sizeof(double)==8);
    size_t read = fread(vals, sizeof(double), cnt, f);
    assert(read == cnt);
    if ( swap )
      pcu_swap_doubles(vals,cnt);
  }

  void readHeader(Reader* r, header* h) {
    const unsigned biggest = 100*1000*1000;
    unsigned headerVals[7];
    readUnsigneds(r->file, headerVals, 7, r->swapBytes);
    for(unsigned i=0; i<7; i++) {
      assert(headerVals[i] < biggest);
    }
    h->nvtx = headerVals[0];
    h->ntri = headerVals[1];
    h->nquad = headerVals[2];
    h->ntet = headerVals[3];
    h->npyr = headerVals[4];
    h->nprz = headerVals[5];
    h->nhex = headerVals[6];
  }

  apf::MeshEntity* makeVtx(Reader* r,
      apf::Vector3& pt, apf::ModelEntity* g) {
    apf::MeshEntity* v = r->mesh->createVert(g);
    r->mesh->setPoint(v, 0, pt);
    return v;
  }

  apf::MeshEntity* lookupVert(Reader* r, long ftnNodeId) {
    const long cNodeId = ftnNodeId - 1;
    assert(r->nodeMap.count(cNodeId));
    return r->nodeMap[cNodeId];
  }

  void readNodes(Reader* r, header* h) {
    const unsigned dim = 3;
    size_t cnt = h->nvtx*dim;
    double* xyz = (double*) calloc(cnt,sizeof(double));
    readDoubles(r->file, xyz, cnt, r->swapBytes);
    for(long id=0; id<h->nvtx; id++) {
      apf::Vector3 p;
      for(unsigned j=0; j<dim; j++)
        p[j] = xyz[id*dim+j];
      r->nodeMap[id] = makeVtx(r,p,0);
    }
    free(xyz);
    fprintf(stderr, "read %d vtx\n", h->nvtx);
  }

  void setNodeIds(Reader* r, header* h) {
    apf::Mesh* m = r->mesh;
    apf::MeshTag* t = m->createLongTag("ugrid-vtx-ids",1);
    for(long id=0; id<h->nvtx; id++)
      m->setLongTag(r->nodeMap[id],t,&id);
    m->destroyTag(t);
  }

  void readFaces(Reader* r, unsigned nfaces, int apfType) {
    const unsigned nverts = apf::Mesh::adjacentCount[apfType][0];
    size_t cnt = nfaces * nverts;
    unsigned* vtx = (unsigned*) calloc(cnt,sizeof(unsigned));
    r->faceVerts[faceTypeIdx(apfType)] = vtx;
    readUnsigneds(r->file, vtx, cnt, r->swapBytes);
  }

  void readTags(Reader* r, unsigned nfaces, int apfType) {
    unsigned* tags = (unsigned*) calloc(nfaces,sizeof(unsigned));
    r->faceTags[faceTypeIdx(apfType)] = tags;
    readUnsigneds(r->file, tags, nfaces, r->swapBytes);
  }

  void readFacesAndTags(Reader* r, header* h) {
    //read the face vertices and store them
    readFaces(r,h->ntri,apf::Mesh::TRIANGLE);
    readFaces(r,h->nquad,apf::Mesh::QUAD);
    //read the tags and store them
    readTags(r,h->ntri,apf::Mesh::TRIANGLE);
    readTags(r,h->nquad,apf::Mesh::QUAD);
  }

  void checkFilePos(Reader* r, header* h) {
    // seven headers, vtx coords, face vertex ids, and face tags
    long expected = h->nvtx*3*sizeof(double) +
      sizeof(unsigned)*(7 + h->ntri*3 + h->nquad*4 + (h->ntri+h->nquad));
    long pos = ftell(r->file);
    assert( pos == expected );
  }

  void setFaceTags(Reader* r, apf::MeshTag* t, unsigned nfaces, int apfType) {
    const unsigned nverts = apf::Mesh::adjacentCount[apfType][0];
    unsigned* vtx = r->faceVerts[faceTypeIdx(apfType)];
    unsigned* tags = r->faceTags[faceTypeIdx(apfType)];
    for(unsigned id=0; id<nfaces; id++) {
      apf::Downward verts;
      for(unsigned j=0; j<nverts; j++) {
        verts[j] = lookupVert(r, vtx[id*nverts+j]);
      }
      apf::MeshEntity* f =
        apf::findElement(r->mesh, apfType, verts);
      assert(f);
      int val = tags[id];
      r->mesh->setIntTag(f, t, &val);
    }
    free(vtx);
    free(tags);
    fprintf(stderr, "set %d %s face tags\n",
        nfaces, apf::Mesh::typeName[apfType]);
  }

  void setFaceTags(Reader* r, header* h) {
    apf::MeshTag* t = r->mesh->createIntTag("ugrid-face-tag", 1);
    setFaceTags(r,t,h->ntri,apf::Mesh::TRIANGLE);
    setFaceTags(r,t,h->nquad,apf::Mesh::QUAD);
  }

  inline unsigned ugridToMdsElmIdx(int apfType, int ugridIdx) {
    static int ugrid_to_mds_verts[4][8] = {
      {0, 1, 2, 3, -1, -1, -1, -1}, //tet
      {0, 1, 2, 3,  4,  5,  6,  7}, //hex
      {0, 1, 2, 3,  4,  5, -1, -1}, //prism
      {3, 2, 4, 0,  1, -1, -1, -1}  //pyramid
    };
    assert(apfType >= 4);
    return ugrid_to_mds_verts[apfType-4][ugridIdx];
  }

  void readElms(Reader* r, unsigned nelms, int apfType) {
    const unsigned nverts = apf::Mesh::adjacentCount[apfType][0];
    apf::ModelEntity* g = r->mesh->findModelEntity(3, 0);
    size_t cnt = nelms*nverts;
    unsigned* vtx = (unsigned*) calloc(cnt,sizeof(unsigned));
    readUnsigneds(r->file, vtx, cnt, r->swapBytes);
    for(unsigned i=0; i<nelms; i++) {
      apf::Downward verts;
      for(unsigned j=0; j<nverts; j++) {
        const unsigned mdsIdx = ugridToMdsElmIdx(apfType,j);
        verts[mdsIdx] = lookupVert(r, vtx[i*nverts+j]);
      }
      apf::MeshEntity* elm =
        apf::buildElement(r->mesh, g, apfType, verts);
      assert(elm);
    }
    free(vtx);
    fprintf(stderr, "read %d %s\n", nelms, apf::Mesh::typeName[apfType]);
  }

  void readElms(Reader* r, header* h) {
    readElms(r,h->ntet,apf::Mesh::TET);
    readElms(r,h->npyr,apf::Mesh::PYRAMID);
    readElms(r,h->nprz,apf::Mesh::PRISM);
    readElms(r,h->nhex,apf::Mesh::HEX);
  }

  void freeReader(Reader* r) {
    fclose(r->file);
  }

  void readUgrid(apf::Mesh2* m, const char* filename)
  {
    header hdr;
    Reader r;
    initReader(&r, m, filename);
    readHeader(&r, &hdr);
    hdr.print();
    readNodes(&r, &hdr);
    setNodeIds(&r, &hdr);
    readFacesAndTags(&r,&hdr);
    checkFilePos(&r,&hdr);
    readElms(&r,&hdr);
    setFaceTags(&r,&hdr);
    freeReader(&r);
    m->acceptChanges();
  }
}

namespace apf {
  Mesh2* loadMdsFromUgrid(gmi_model* g, const char* filename)
  {
    Mesh2* m = makeEmptyMdsMesh(g, 0, false);
    apf::changeMdsDimension(m, 3);
    readUgrid(m, filename);
    fprintf(stderr,"vtx %lu edge %lu face %lu rgn %lu\n",
        m->count(0), m->count(1), m->count(2), m->count(3));
    return m;
  }
}
<commit_msg>long tags are not written, don't destroy the tag<commit_after>#include "apf.h"
#include "apfMDS.h"
#include "apfMesh2.h"
#include "pcu_io.h"
#include "pcu_byteorder.h"

#include <cstdio>
#include <cstring>
#include <cassert>
#include <cstdlib>

/*
read files in the AFLR3 format from Dave Marcum at Mississippi State
-little-endian if file has suffix '.lb8.ugrid'
-big-endian if file has suffix '.b8.ugrid'
-integers are 4B
-floats are 8B
*/

namespace {
  int faceTypeIdx(int type) {
    switch(type) {
      case apf::Mesh::TRIANGLE: return 0;
      case apf::Mesh::QUAD: return 1;
      default: {
                 assert(false);
                 return -1;
               }
    }
  }

  struct Reader {
    apf::Mesh2* mesh;
    FILE* file;
    std::map<long, apf::MeshEntity*> nodeMap;
    unsigned* faceVerts[2];
    unsigned* faceTags[2];
    bool swapBytes;
  };

  struct header {
    unsigned nvtx, ntri, nquad, ntet, npyr, nprz, nhex;
    void print() {
      fprintf(stderr,
          "nvtx %u ntri %u nquad %u ntet %u npyr %u nprz %u nhex %u\n",
          nvtx,ntri,nquad,ntet,npyr,nprz,nhex);
    }
  };

  void initReader(Reader* r, apf::Mesh2* m, const char* filename) {
    r->mesh = m;
    r->file = fopen(filename, "rb");
    if (!r->file) {
      fprintf(stderr,"ERROR couldn't open ugrid file \"%s\"\n",filename);
      abort();
    }
    unsigned endian = -1;
    if ( strstr(filename, ".b8.ugrid") ) {
      endian = PCU_BIG_ENDIAN;
    } else if ( strstr(filename, ".lb8.ugrid") ) {
      endian = PCU_LITTLE_ENDIAN;
    } else {
      fprintf(stderr,
          "ERROR file extension of \"%s\" is not supported\n", filename);
      exit(EXIT_FAILURE);
    }
    r->swapBytes = ( endian != PCU_HOST_ORDER );
  }

  void readUnsigneds(FILE* f, unsigned* v, size_t cnt, bool swap) {
    assert(sizeof(unsigned)==4);
    size_t read = fread(v, sizeof(unsigned), cnt, f);
    assert(read == cnt);
    if ( swap )
      pcu_swap_unsigneds(v,cnt);
  }

  void readDoubles(FILE* f, double* vals, size_t cnt, bool swap) {
    assert(sizeof(double)==8);
    size_t read = fread(vals, sizeof(double), cnt, f);
    assert(read == cnt);
    if ( swap )
      pcu_swap_doubles(vals,cnt);
  }

  void readHeader(Reader* r, header* h) {
    const unsigned biggest = 100*1000*1000;
    unsigned headerVals[7];
    readUnsigneds(r->file, headerVals, 7, r->swapBytes);
    for(unsigned i=0; i<7; i++) {
      assert(headerVals[i] < biggest);
    }
    h->nvtx = headerVals[0];
    h->ntri = headerVals[1];
    h->nquad = headerVals[2];
    h->ntet = headerVals[3];
    h->npyr = headerVals[4];
    h->nprz = headerVals[5];
    h->nhex = headerVals[6];
  }

  apf::MeshEntity* makeVtx(Reader* r,
      apf::Vector3& pt, apf::ModelEntity* g) {
    apf::MeshEntity* v = r->mesh->createVert(g);
    r->mesh->setPoint(v, 0, pt);
    return v;
  }

  apf::MeshEntity* lookupVert(Reader* r, long ftnNodeId) {
    const long cNodeId = ftnNodeId - 1;
    assert(r->nodeMap.count(cNodeId));
    return r->nodeMap[cNodeId];
  }

  void readNodes(Reader* r, header* h) {
    const unsigned dim = 3;
    size_t cnt = h->nvtx*dim;
    double* xyz = (double*) calloc(cnt,sizeof(double));
    readDoubles(r->file, xyz, cnt, r->swapBytes);
    for(long id=0; id<h->nvtx; id++) {
      apf::Vector3 p;
      for(unsigned j=0; j<dim; j++)
        p[j] = xyz[id*dim+j];
      r->nodeMap[id] = makeVtx(r,p,0);
    }
    free(xyz);
    fprintf(stderr, "read %d vtx\n", h->nvtx);
  }

  void setNodeIds(Reader* r, header* h) {
    apf::Mesh* m = r->mesh;
    apf::MeshTag* t = m->createIntTag("ugrid-vtx-ids",1);
    for(long lid=0; lid<h->nvtx; lid++) {
      int iid = lid;
      m->setIntTag(r->nodeMap[lid],t,&iid);
    }
  }

  void readFaces(Reader* r, unsigned nfaces, int apfType) {
    const unsigned nverts = apf::Mesh::adjacentCount[apfType][0];
    size_t cnt = nfaces * nverts;
    unsigned* vtx = (unsigned*) calloc(cnt,sizeof(unsigned));
    r->faceVerts[faceTypeIdx(apfType)] = vtx;
    readUnsigneds(r->file, vtx, cnt, r->swapBytes);
  }

  void readTags(Reader* r, unsigned nfaces, int apfType) {
    unsigned* tags = (unsigned*) calloc(nfaces,sizeof(unsigned));
    r->faceTags[faceTypeIdx(apfType)] = tags;
    readUnsigneds(r->file, tags, nfaces, r->swapBytes);
  }

  void readFacesAndTags(Reader* r, header* h) {
    //read the face vertices and store them
    readFaces(r,h->ntri,apf::Mesh::TRIANGLE);
    readFaces(r,h->nquad,apf::Mesh::QUAD);
    //read the tags and store them
    readTags(r,h->ntri,apf::Mesh::TRIANGLE);
    readTags(r,h->nquad,apf::Mesh::QUAD);
  }

  void checkFilePos(Reader* r, header* h) {
    // seven headers, vtx coords, face vertex ids, and face tags
    long expected = h->nvtx*3*sizeof(double) +
      sizeof(unsigned)*(7 + h->ntri*3 + h->nquad*4 + (h->ntri+h->nquad));
    long pos = ftell(r->file);
    assert( pos == expected );
  }

  void setFaceTags(Reader* r, apf::MeshTag* t, unsigned nfaces, int apfType) {
    const unsigned nverts = apf::Mesh::adjacentCount[apfType][0];
    unsigned* vtx = r->faceVerts[faceTypeIdx(apfType)];
    unsigned* tags = r->faceTags[faceTypeIdx(apfType)];
    for(unsigned id=0; id<nfaces; id++) {
      apf::Downward verts;
      for(unsigned j=0; j<nverts; j++) {
        verts[j] = lookupVert(r, vtx[id*nverts+j]);
      }
      apf::MeshEntity* f =
        apf::findElement(r->mesh, apfType, verts);
      assert(f);
      int val = tags[id];
      r->mesh->setIntTag(f, t, &val);
    }
    free(vtx);
    free(tags);
    fprintf(stderr, "set %d %s face tags\n",
        nfaces, apf::Mesh::typeName[apfType]);
  }

  void setFaceTags(Reader* r, header* h) {
    apf::MeshTag* t = r->mesh->createIntTag("ugrid-face-tag", 1);
    setFaceTags(r,t,h->ntri,apf::Mesh::TRIANGLE);
    setFaceTags(r,t,h->nquad,apf::Mesh::QUAD);
  }

  inline unsigned ugridToMdsElmIdx(int apfType, int ugridIdx) {
    static int ugrid_to_mds_verts[4][8] = {
      {0, 1, 2, 3, -1, -1, -1, -1}, //tet
      {0, 1, 2, 3,  4,  5,  6,  7}, //hex
      {0, 1, 2, 3,  4,  5, -1, -1}, //prism
      {3, 2, 4, 0,  1, -1, -1, -1}  //pyramid
    };
    assert(apfType >= 4);
    return ugrid_to_mds_verts[apfType-4][ugridIdx];
  }

  void readElms(Reader* r, unsigned nelms, int apfType) {
    const unsigned nverts = apf::Mesh::adjacentCount[apfType][0];
    apf::ModelEntity* g = r->mesh->findModelEntity(3, 0);
    size_t cnt = nelms*nverts;
    unsigned* vtx = (unsigned*) calloc(cnt,sizeof(unsigned));
    readUnsigneds(r->file, vtx, cnt, r->swapBytes);
    for(unsigned i=0; i<nelms; i++) {
      apf::Downward verts;
      for(unsigned j=0; j<nverts; j++) {
        const unsigned mdsIdx = ugridToMdsElmIdx(apfType,j);
        verts[mdsIdx] = lookupVert(r, vtx[i*nverts+j]);
      }
      apf::MeshEntity* elm =
        apf::buildElement(r->mesh, g, apfType, verts);
      assert(elm);
    }
    free(vtx);
    fprintf(stderr, "read %d %s\n", nelms, apf::Mesh::typeName[apfType]);
  }

  void readElms(Reader* r, header* h) {
    readElms(r,h->ntet,apf::Mesh::TET);
    readElms(r,h->npyr,apf::Mesh::PYRAMID);
    readElms(r,h->nprz,apf::Mesh::PRISM);
    readElms(r,h->nhex,apf::Mesh::HEX);
  }

  void freeReader(Reader* r) {
    fclose(r->file);
  }

  void readUgrid(apf::Mesh2* m, const char* filename)
  {
    header hdr;
    Reader r;
    initReader(&r, m, filename);
    readHeader(&r, &hdr);
    hdr.print();
    readNodes(&r, &hdr);
    setNodeIds(&r, &hdr);
    readFacesAndTags(&r,&hdr);
    checkFilePos(&r,&hdr);
    readElms(&r,&hdr);
    setFaceTags(&r,&hdr);
    freeReader(&r);
    m->acceptChanges();
  }
}

namespace apf {
  Mesh2* loadMdsFromUgrid(gmi_model* g, const char* filename)
  {
    Mesh2* m = makeEmptyMdsMesh(g, 0, false);
    apf::changeMdsDimension(m, 3);
    readUgrid(m, filename);
    fprintf(stderr,"vtx %lu edge %lu face %lu rgn %lu\n",
        m->count(0), m->count(1), m->count(2), m->count(3));
    return m;
  }
}
<|endoftext|>


<commit_before>/* Copyright 2015 Realm Inc - All Rights Reserved
 * Proprietary and Confidential
 */

#include <realm.hpp>
#include <realm/lang_bind_helper.hpp>
#include "error_handling.hpp"
#include "realm_export_decls.hpp"
#include "marshalling.hpp"
#include "object-store/src/shared_realm.hpp"
#include "object-store/src/schema.hpp"
#include "object-store/src/binding_context.hpp"
#include <list>

#ifdef REALM_PLATFORM_ANDROID
#include "object-store/src/impl/android/weak_realm_notifier.hpp"
#endif

using namespace realm;
using namespace realm::binding;

using NotifyRealmChangedT = void(*)(void* managed_realm_handle);
NotifyRealmChangedT notify_realm_changed = nullptr;

namespace realm {
namespace binding {

class CSharpBindingContext: public BindingContext {
public:
    CSharpBindingContext(void* managed_realm_handle) : m_managed_realm_handle(managed_realm_handle) {}

    void did_change(std::vector<ObserverState> const&, std::vector<void*> const&) override
    {
        notify_realm_changed(m_managed_realm_handle);
    }

private:
    void* m_managed_realm_handle;
};

}
}

extern "C" {

REALM_EXPORT void register_notify_realm_changed(NotifyRealmChangedT notifier)
{
    notify_realm_changed = notifier;
}

REALM_EXPORT SharedRealm* shared_realm_open(Schema* schema, uint16_t* path, size_t path_len, bool read_only, SharedGroup::DurabilityLevel durability,
                        uint8_t* encryption_key, uint64_t schemaVersion)
{
    return handle_errors([&]() {
        Utf16StringAccessor pathStr(path, path_len);

        Realm::Config config;
        config.path = pathStr.to_string();
        config.read_only = read_only;
        config.in_memory = durability != SharedGroup::durability_Full;

        // by definition the key is only allowwed to be 64 bytes long, enforced by C# code
        if (encryption_key == nullptr)
          config.encryption_key = std::vector<char>();
        else
          config.encryption_key = std::vector<char>(encryption_key, encryption_key+64);

        config.schema.reset(schema);
        config.schema_version = schemaVersion;

        return new SharedRealm{Realm::get_shared_realm(config)};
    });
}

REALM_EXPORT void shared_realm_bind_to_managed_realm_handle(SharedRealm* realm, void* managed_realm_handle)
{
    handle_errors([&]() {
        (*realm)->m_binding_context = std::unique_ptr<realm::BindingContext>(new CSharpBindingContext(managed_realm_handle));
    });
}

REALM_EXPORT void shared_realm_destroy(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->close();
        delete realm;
    });
}

REALM_EXPORT size_t shared_realm_has_table(SharedRealm* realm, uint16_t* table_name, size_t table_name_len)
{
    return handle_errors([&]() {
        Group* g = (*realm)->read_group();
        Utf16StringAccessor str(table_name, table_name_len);

        return bool_to_size_t(g->has_table(str));
    });
}

REALM_EXPORT Table* shared_realm_get_table(SharedRealm* realm, uint16_t* table_name, size_t table_name_len)
{
    return handle_errors([&]() {
      Group* g = (*realm)->read_group();
      Utf16StringAccessor str(table_name, table_name_len);

      bool dummy; // get_or_add_table sets this to true if the table was added.
      return LangBindHelper::get_or_add_table(*g, str, &dummy);
    });
}

REALM_EXPORT uint64_t  shared_realm_get_schema_version(SharedRealm* realm)
{
    return handle_errors([&]() {
      return (*realm)->config().schema_version;
    });
}

REALM_EXPORT void shared_realm_begin_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->begin_transaction();
    });
}

REALM_EXPORT void shared_realm_commit_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->commit_transaction();
    });
}

REALM_EXPORT void shared_realm_cancel_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->cancel_transaction();
    });
}

REALM_EXPORT size_t shared_realm_is_in_transaction(SharedRealm* realm)
{
    return handle_errors([&]() {
        return bool_to_size_t((*realm)->is_in_transaction());
    });
}

REALM_EXPORT size_t shared_realm_is_same_instance(SharedRealm* lhs, SharedRealm* rhs)
{
    return handle_errors([&]() {
        return *lhs == *rhs;  // just compare raw pointers inside the smart pointers
    });
}

REALM_EXPORT size_t shared_realm_refresh(SharedRealm* realm)
{
    return handle_errors([&]() {
        return bool_to_size_t((*realm)->refresh());
    });
}

#ifdef REALM_PLATFORM_ANDROID

REALM_EXPORT void bind_handler_functions(realm::_impl::create_handler_function create_function,
    realm::_impl::notify_handler_function notify_function,
    realm::_impl::destroy_handler_function destroy_function)
{
    handle_errors([&]() {
        realm::_impl::create_handler_for_current_thread = create_function;
        realm::_impl::notify_handler = notify_function;
        realm::_impl::destroy_handler = destroy_function;
    });
}

REALM_EXPORT void notify_realm(Realm* realm)
{
    handle_errors([&]() {
        realm->notify();
    });
}

#endif

}
<commit_msg>Make notify_realm accept a pointer to a weak_ptr<commit_after>/* Copyright 2015 Realm Inc - All Rights Reserved
 * Proprietary and Confidential
 */

#include <realm.hpp>
#include <realm/lang_bind_helper.hpp>
#include "error_handling.hpp"
#include "realm_export_decls.hpp"
#include "marshalling.hpp"
#include "object-store/src/shared_realm.hpp"
#include "object-store/src/schema.hpp"
#include "object-store/src/binding_context.hpp"
#include <list>

#ifdef REALM_PLATFORM_ANDROID
#include "object-store/src/impl/android/weak_realm_notifier.hpp"
#endif

using namespace realm;
using namespace realm::binding;

using NotifyRealmChangedT = void(*)(void* managed_realm_handle);
NotifyRealmChangedT notify_realm_changed = nullptr;

namespace realm {
namespace binding {

class CSharpBindingContext: public BindingContext {
public:
    CSharpBindingContext(void* managed_realm_handle) : m_managed_realm_handle(managed_realm_handle) {}

    void did_change(std::vector<ObserverState> const&, std::vector<void*> const&) override
    {
        notify_realm_changed(m_managed_realm_handle);
    }

private:
    void* m_managed_realm_handle;
};

}
}

extern "C" {

REALM_EXPORT void register_notify_realm_changed(NotifyRealmChangedT notifier)
{
    notify_realm_changed = notifier;
}

REALM_EXPORT SharedRealm* shared_realm_open(Schema* schema, uint16_t* path, size_t path_len, bool read_only, SharedGroup::DurabilityLevel durability,
                        uint8_t* encryption_key, uint64_t schemaVersion)
{
    return handle_errors([&]() {
        Utf16StringAccessor pathStr(path, path_len);

        Realm::Config config;
        config.path = pathStr.to_string();
        config.read_only = read_only;
        config.in_memory = durability != SharedGroup::durability_Full;

        // by definition the key is only allowwed to be 64 bytes long, enforced by C# code
        if (encryption_key == nullptr)
          config.encryption_key = std::vector<char>();
        else
          config.encryption_key = std::vector<char>(encryption_key, encryption_key+64);

        config.schema.reset(schema);
        config.schema_version = schemaVersion;

        return new SharedRealm{Realm::get_shared_realm(config)};
    });
}

REALM_EXPORT void shared_realm_bind_to_managed_realm_handle(SharedRealm* realm, void* managed_realm_handle)
{
    handle_errors([&]() {
        (*realm)->m_binding_context = std::unique_ptr<realm::BindingContext>(new CSharpBindingContext(managed_realm_handle));
    });
}

REALM_EXPORT void shared_realm_destroy(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->close();
        delete realm;
    });
}

REALM_EXPORT size_t shared_realm_has_table(SharedRealm* realm, uint16_t* table_name, size_t table_name_len)
{
    return handle_errors([&]() {
        Group* g = (*realm)->read_group();
        Utf16StringAccessor str(table_name, table_name_len);

        return bool_to_size_t(g->has_table(str));
    });
}

REALM_EXPORT Table* shared_realm_get_table(SharedRealm* realm, uint16_t* table_name, size_t table_name_len)
{
    return handle_errors([&]() {
      Group* g = (*realm)->read_group();
      Utf16StringAccessor str(table_name, table_name_len);

      bool dummy; // get_or_add_table sets this to true if the table was added.
      return LangBindHelper::get_or_add_table(*g, str, &dummy);
    });
}

REALM_EXPORT uint64_t  shared_realm_get_schema_version(SharedRealm* realm)
{
    return handle_errors([&]() {
      return (*realm)->config().schema_version;
    });
}

REALM_EXPORT void shared_realm_begin_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->begin_transaction();
    });
}

REALM_EXPORT void shared_realm_commit_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->commit_transaction();
    });
}

REALM_EXPORT void shared_realm_cancel_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->cancel_transaction();
    });
}

REALM_EXPORT size_t shared_realm_is_in_transaction(SharedRealm* realm)
{
    return handle_errors([&]() {
        return bool_to_size_t((*realm)->is_in_transaction());
    });
}

REALM_EXPORT size_t shared_realm_is_same_instance(SharedRealm* lhs, SharedRealm* rhs)
{
    return handle_errors([&]() {
        return *lhs == *rhs;  // just compare raw pointers inside the smart pointers
    });
}

REALM_EXPORT size_t shared_realm_refresh(SharedRealm* realm)
{
    return handle_errors([&]() {
        return bool_to_size_t((*realm)->refresh());
    });
}

#ifdef REALM_PLATFORM_ANDROID

REALM_EXPORT void bind_handler_functions(realm::_impl::create_handler_function create_function,
    realm::_impl::notify_handler_function notify_function,
    realm::_impl::destroy_handler_function destroy_function)
{
    handle_errors([&]() {
        realm::_impl::create_handler_for_current_thread = create_function;
        realm::_impl::notify_handler = notify_function;
        realm::_impl::destroy_handler = destroy_function;
    });
}

REALM_EXPORT void notify_realm(std::weak_ptr<Realm>* realm)
{
    handle_errors([&]() {
        if (auto realm_ptr = realm->lock()) {
            if (!realm_ptr->is_closed())
                realm_ptr->notify();
        }

        delete realm;
    });
}

#endif

}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// Optimize combinations of instructions
//

#include <algorithm>

#include <wasm.h>
#include <pass.h>
#include <wasm-s-parser.h>
#include <support/threads.h>
#include <ast_utils.h>

namespace wasm {

Name I32_EXPR  = "i32.expr",
     I64_EXPR  = "i64.expr",
     F32_EXPR  = "f32.expr",
     F64_EXPR  = "f64.expr",
     ANY_EXPR  = "any.expr";

// A pattern
struct Pattern {
  Expression* input;
  Expression* output;

  Pattern(Expression* input, Expression* output) : input(input), output(output) {}
};

// Database of patterns
struct PatternDatabase {
  Module wasm;

  char* input;

  std::map<Expression::Id, std::vector<Pattern>> patternMap; // root expression id => list of all patterns for it TODO optimize more

  PatternDatabase() {
    // TODO: do this on first use, with a lock, to avoid startup pause
    // generate module
    input = strdup(
      #include "OptimizeInstructions.wast.processed"
    );
    SExpressionParser parser(input);
    Element& root = *parser.root;
    SExpressionWasmBuilder builder(wasm, *root[0]);
    // parse module form
    auto* func = wasm.getFunction("patterns");
    auto* body = func->body->cast<Block>();
    for (auto* item : body->list) {
      auto* pair = item->cast<Block>();
      patternMap[pair->list[0]->_id].emplace_back(pair->list[0], pair->list[1]);
    }
  }

  ~PatternDatabase() {
    free(input);
  };
};

static PatternDatabase* database = nullptr;

static void ensureDatabase() {
  if (!database) {
    // we must only ever create one database
    static OnlyOnce onlyOnce;
    onlyOnce.verify();
    database = new PatternDatabase;
  }
}

// Check for matches and apply them
struct Match {
  Module& wasm;
  Pattern& pattern;

  Match(Module& wasm, Pattern& pattern) : wasm(wasm), pattern(pattern) {}

  std::vector<Expression*> wildcards; // id in i32.any(id) etc. => the expression it represents in this match

  // Comparing/checking

  // Check if we can match to this pattern, updating ourselves with the info if so
  bool check(Expression* seen) {
    // compare seen to the pattern input, doing a special operation for our "wildcards"
    assert(wildcards.size() == 0);
    return ExpressionAnalyzer::flexibleEqual(pattern.input, seen, *this);
  }

  bool compare(Expression* subInput, Expression* subSeen) {
    CallImport* call = subInput->dynCast<CallImport>();
    if (!call || call->operands.size() != 1 || call->operands[0]->type != i32 || !call->operands[0]->is<Const>()) return false;
    Index index = call->operands[0]->cast<Const>()->value.geti32();
    // handle our special functions
    auto checkMatch = [&](WasmType type) {
      if (type != none && subSeen->type != type) return false;
      while (index >= wildcards.size()) {
        wildcards.push_back(nullptr);
      }
      if (!wildcards[index]) {
        // new wildcard
        wildcards[index] = subSeen; // NB: no need to copy
        return true;
      } else {
        // We are seeing this index for a second or later time, check it matches
        return ExpressionAnalyzer::equal(subSeen, wildcards[index]);
      };
    };
    if (call->target == I32_EXPR) {
      if (checkMatch(i32)) return true;
    } else if (call->target == I64_EXPR) {
      if (checkMatch(i64)) return true;
    } else if (call->target == F32_EXPR) {
      if (checkMatch(f32)) return true;
    } else if (call->target == F64_EXPR) {
      if (checkMatch(f64)) return true;
    } else if (call->target == ANY_EXPR) {
      if (checkMatch(none)) return true;
    }
    return false;
  }

  // Applying/copying

  // Apply the match, generate an output expression from the matched input, performing substitutions as necessary
  Expression* apply() {
    return ExpressionManipulator::flexibleCopy(pattern.output, wasm, *this);
  }

  // When copying a wildcard, perform the substitution.
  // TODO: we can reuse nodes, not copying a wildcard when it appears just once, and we can reuse other individual nodes when they are discarded anyhow.
  Expression* copy(Expression* curr) {
    CallImport* call = curr->dynCast<CallImport>();
    if (!call || call->operands.size() != 1 || call->operands[0]->type != i32 || !call->operands[0]->is<Const>()) return nullptr;
    Index index = call->operands[0]->cast<Const>()->value.geti32();
    // handle our special functions
    if (call->target == I32_EXPR || call->target == I64_EXPR || call->target == F32_EXPR || call->target == F64_EXPR || call->target == ANY_EXPR) {
      return ExpressionManipulator::copy(wildcards.at(index), wasm);
    }
    return nullptr;
  }
};

// Main pass class
struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, UnifiedExpressionVisitor<OptimizeInstructions>>> {
  bool isFunctionParallel() override { return true; }

  Pass* create() override { return new OptimizeInstructions; }

  OptimizeInstructions() {
    ensureDatabase();
  }

  void visitExpression(Expression* curr) {
    // we may be able to apply multiple patterns, one may open opportunities that look deeper NB: patterns must not have cycles
    while (1) {
      auto* handOptimized = handOptimize(curr);
      if (handOptimized) {
        curr = handOptimized;
        replaceCurrent(curr);
      }
      auto iter = database->patternMap.find(curr->_id);
      if (iter == database->patternMap.end()) return;
      auto& patterns = iter->second;
      bool more = false;
      for (auto& pattern : patterns) {
        Match match(*getModule(), pattern);
        if (match.check(curr)) {
          curr = match.apply();
          replaceCurrent(curr);
          more = true;
          break; // exit pattern for loop, return to main while loop
        }
      }
      if (!more) break;
    }
  }

  // Optimizations that don't yet fit in the pattern DSL, but could be eventually maybe
  Expression* handOptimize(Expression* curr) {
    if (auto* binary = curr->dynCast<Binary>()) {
      // pattern match a load of 8 bits and a sign extend using a shl of 24 then shr_s of 24 as well, etc.
      if (binary->op == BinaryOp::ShrSInt32 && binary->right->is<Const>()) {
        auto shifts = binary->right->cast<Const>()->value.geti32();
        if (shifts == 24 || shifts == 16) {
          auto* left = binary->left->dynCast<Binary>();
          if (left && left->op == ShlInt32 && left->right->is<Const>() && left->right->cast<Const>()->value.geti32() == shifts) {
            auto* load = left->left->dynCast<Load>();
            if (load && ((load->bytes == 1 && shifts == 24) || (load->bytes == 2 && shifts == 16))) {
              load->signed_ = true;
              return load;
            }
          }
        }
      }
    } else if (auto* set = curr->dynCast<SetGlobal>()) {
      // optimize out a set of a get
      auto* get = set->value->dynCast<GetGlobal>();
      if (get && get->name == set->name) {
        ExpressionManipulator::nop(curr);
      }
    } else if (auto* iff = curr->dynCast<If>()) {
      iff->condition = optimizeBoolean(iff->condition);
    } else if (auto* select = curr->dynCast<Select>()) {
      select->condition = optimizeBoolean(select->condition);
      auto* condition = select->condition->dynCast<Unary>();
      if (condition && condition->op == EqZInt32) {
        // flip select to remove eqz, if we can reorder
        EffectAnalyzer ifTrue(select->ifTrue);
        EffectAnalyzer ifFalse(select->ifFalse);
        if (!ifTrue.invalidates(ifFalse)) {
          select->condition = condition->value;
          std::swap(select->ifTrue, select->ifFalse);
        }
      }
    } else if (auto* br = curr->dynCast<Break>()) {
      if (br->condition) {
        br->condition = optimizeBoolean(br->condition);
      }
    }
    return nullptr;
  }

private:

  Expression* optimizeBoolean(Expression* boolean) {
    auto* condition = boolean->dynCast<Unary>();
    if (condition && condition->op == EqZInt32) {
      auto* condition2 = condition->value->dynCast<Unary>();
      if (condition2 && condition2->op == EqZInt32) {
        // double eqz
        return condition2->value;
      }
    }
    return boolean;
  }
};

Pass *createOptimizeInstructionsPass() {
  return new OptimizeInstructions();
}

} // namespace wasm
<commit_msg>clean up database-ensuring code using the new prepareToRun method<commit_after>/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// Optimize combinations of instructions
//

#include <algorithm>

#include <wasm.h>
#include <pass.h>
#include <wasm-s-parser.h>
#include <support/threads.h>
#include <ast_utils.h>

namespace wasm {

Name I32_EXPR  = "i32.expr",
     I64_EXPR  = "i64.expr",
     F32_EXPR  = "f32.expr",
     F64_EXPR  = "f64.expr",
     ANY_EXPR  = "any.expr";

// A pattern
struct Pattern {
  Expression* input;
  Expression* output;

  Pattern(Expression* input, Expression* output) : input(input), output(output) {}
};

// Database of patterns
struct PatternDatabase {
  Module wasm;

  char* input;

  std::map<Expression::Id, std::vector<Pattern>> patternMap; // root expression id => list of all patterns for it TODO optimize more

  PatternDatabase() {
    // generate module
    input = strdup(
      #include "OptimizeInstructions.wast.processed"
    );
    SExpressionParser parser(input);
    Element& root = *parser.root;
    SExpressionWasmBuilder builder(wasm, *root[0]);
    // parse module form
    auto* func = wasm.getFunction("patterns");
    auto* body = func->body->cast<Block>();
    for (auto* item : body->list) {
      auto* pair = item->cast<Block>();
      patternMap[pair->list[0]->_id].emplace_back(pair->list[0], pair->list[1]);
    }
  }

  ~PatternDatabase() {
    free(input);
  };
};

static PatternDatabase* database = nullptr;

struct DatabaseEnsurer {
  DatabaseEnsurer() {
    assert(!database);
    database = new PatternDatabase;
  }
};

// Check for matches and apply them
struct Match {
  Module& wasm;
  Pattern& pattern;

  Match(Module& wasm, Pattern& pattern) : wasm(wasm), pattern(pattern) {}

  std::vector<Expression*> wildcards; // id in i32.any(id) etc. => the expression it represents in this match

  // Comparing/checking

  // Check if we can match to this pattern, updating ourselves with the info if so
  bool check(Expression* seen) {
    // compare seen to the pattern input, doing a special operation for our "wildcards"
    assert(wildcards.size() == 0);
    return ExpressionAnalyzer::flexibleEqual(pattern.input, seen, *this);
  }

  bool compare(Expression* subInput, Expression* subSeen) {
    CallImport* call = subInput->dynCast<CallImport>();
    if (!call || call->operands.size() != 1 || call->operands[0]->type != i32 || !call->operands[0]->is<Const>()) return false;
    Index index = call->operands[0]->cast<Const>()->value.geti32();
    // handle our special functions
    auto checkMatch = [&](WasmType type) {
      if (type != none && subSeen->type != type) return false;
      while (index >= wildcards.size()) {
        wildcards.push_back(nullptr);
      }
      if (!wildcards[index]) {
        // new wildcard
        wildcards[index] = subSeen; // NB: no need to copy
        return true;
      } else {
        // We are seeing this index for a second or later time, check it matches
        return ExpressionAnalyzer::equal(subSeen, wildcards[index]);
      };
    };
    if (call->target == I32_EXPR) {
      if (checkMatch(i32)) return true;
    } else if (call->target == I64_EXPR) {
      if (checkMatch(i64)) return true;
    } else if (call->target == F32_EXPR) {
      if (checkMatch(f32)) return true;
    } else if (call->target == F64_EXPR) {
      if (checkMatch(f64)) return true;
    } else if (call->target == ANY_EXPR) {
      if (checkMatch(none)) return true;
    }
    return false;
  }

  // Applying/copying

  // Apply the match, generate an output expression from the matched input, performing substitutions as necessary
  Expression* apply() {
    return ExpressionManipulator::flexibleCopy(pattern.output, wasm, *this);
  }

  // When copying a wildcard, perform the substitution.
  // TODO: we can reuse nodes, not copying a wildcard when it appears just once, and we can reuse other individual nodes when they are discarded anyhow.
  Expression* copy(Expression* curr) {
    CallImport* call = curr->dynCast<CallImport>();
    if (!call || call->operands.size() != 1 || call->operands[0]->type != i32 || !call->operands[0]->is<Const>()) return nullptr;
    Index index = call->operands[0]->cast<Const>()->value.geti32();
    // handle our special functions
    if (call->target == I32_EXPR || call->target == I64_EXPR || call->target == F32_EXPR || call->target == F64_EXPR || call->target == ANY_EXPR) {
      return ExpressionManipulator::copy(wildcards.at(index), wasm);
    }
    return nullptr;
  }
};

// Main pass class
struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, UnifiedExpressionVisitor<OptimizeInstructions>>> {
  bool isFunctionParallel() override { return true; }

  Pass* create() override { return new OptimizeInstructions; }

  void prepareToRun(PassRunner* runner, Module* module) override {
    static DatabaseEnsurer ensurer;
  }

  void visitExpression(Expression* curr) {
    // we may be able to apply multiple patterns, one may open opportunities that look deeper NB: patterns must not have cycles
    while (1) {
      auto* handOptimized = handOptimize(curr);
      if (handOptimized) {
        curr = handOptimized;
        replaceCurrent(curr);
      }
      auto iter = database->patternMap.find(curr->_id);
      if (iter == database->patternMap.end()) return;
      auto& patterns = iter->second;
      bool more = false;
      for (auto& pattern : patterns) {
        Match match(*getModule(), pattern);
        if (match.check(curr)) {
          curr = match.apply();
          replaceCurrent(curr);
          more = true;
          break; // exit pattern for loop, return to main while loop
        }
      }
      if (!more) break;
    }
  }

  // Optimizations that don't yet fit in the pattern DSL, but could be eventually maybe
  Expression* handOptimize(Expression* curr) {
    if (auto* binary = curr->dynCast<Binary>()) {
      // pattern match a load of 8 bits and a sign extend using a shl of 24 then shr_s of 24 as well, etc.
      if (binary->op == BinaryOp::ShrSInt32 && binary->right->is<Const>()) {
        auto shifts = binary->right->cast<Const>()->value.geti32();
        if (shifts == 24 || shifts == 16) {
          auto* left = binary->left->dynCast<Binary>();
          if (left && left->op == ShlInt32 && left->right->is<Const>() && left->right->cast<Const>()->value.geti32() == shifts) {
            auto* load = left->left->dynCast<Load>();
            if (load && ((load->bytes == 1 && shifts == 24) || (load->bytes == 2 && shifts == 16))) {
              load->signed_ = true;
              return load;
            }
          }
        }
      }
    } else if (auto* set = curr->dynCast<SetGlobal>()) {
      // optimize out a set of a get
      auto* get = set->value->dynCast<GetGlobal>();
      if (get && get->name == set->name) {
        ExpressionManipulator::nop(curr);
      }
    } else if (auto* iff = curr->dynCast<If>()) {
      iff->condition = optimizeBoolean(iff->condition);
    } else if (auto* select = curr->dynCast<Select>()) {
      select->condition = optimizeBoolean(select->condition);
      auto* condition = select->condition->dynCast<Unary>();
      if (condition && condition->op == EqZInt32) {
        // flip select to remove eqz, if we can reorder
        EffectAnalyzer ifTrue(select->ifTrue);
        EffectAnalyzer ifFalse(select->ifFalse);
        if (!ifTrue.invalidates(ifFalse)) {
          select->condition = condition->value;
          std::swap(select->ifTrue, select->ifFalse);
        }
      }
    } else if (auto* br = curr->dynCast<Break>()) {
      if (br->condition) {
        br->condition = optimizeBoolean(br->condition);
      }
    }
    return nullptr;
  }

private:

  Expression* optimizeBoolean(Expression* boolean) {
    auto* condition = boolean->dynCast<Unary>();
    if (condition && condition->op == EqZInt32) {
      auto* condition2 = condition->value->dynCast<Unary>();
      if (condition2 && condition2->op == EqZInt32) {
        // double eqz
        return condition2->value;
      }
    }
    return boolean;
  }
};

Pass *createOptimizeInstructionsPass() {
  return new OptimizeInstructions();
}

} // namespace wasm
<|endoftext|>


<commit_before>#pragma once

#include "../../types.hpp"
#include "../base.hpp"
#include "counter.hpp"
#include <nlohmann/json.hpp>
#include <pqrs/dispatcher.hpp>

namespace krbn {
namespace manipulator {
namespace manipulators {
namespace mouse_motion_to_scroll {
class mouse_motion_to_scroll final : public base, public pqrs::dispatcher::extra::dispatcher_client {
public:
  mouse_motion_to_scroll(const nlohmann::json& json,
                         const core_configuration::details::complex_modifications_parameters& parameters) : base(),
                                                                                dispatcher_client() {
    try {
      if (!json.is_object()) {
        throw pqrs::json::unmarshal_error(fmt::format("json must be object, but is `{0}`", json.dump()));
      }

      for (const auto& [key, value] : json.items()) {
        if (key == "from") {
          if (!value.is_object()) {
            throw pqrs::json::unmarshal_error(fmt::format("`{0}` must be object, but is `{1}`", key, value.dump()));
          }

          for (const auto& [k, v] : value.items()) {
            if (k == "modifiers") {
              try {
                from_modifiers_definition_ = v.get<from_modifiers_definition>();
              } catch (const pqrs::json::unmarshal_error& e) {
                throw pqrs::json::unmarshal_error(fmt::format("`{0}.{1}` error: {2}", key, k, e.what()));
              }
            }
          }

        } else if (key == "options") {
          for (const auto& [k, v] : value.items()) {
            if (k == "threshold") { // (secret parameter)
              counter_parameters_.set_threshold(v.get<int>());
            } else if (k == "recent_time_duration") {
              counter_parameters_.recent_time_duration =
                  pqrs::osx::chrono::make_absolute_time_duration(
                      std::chrono::milliseconds(v.get<int>()));
            } else if (k == "value_scale") {
              counter_parameters_.value_scale = v.get<double>();
            } else if (k == "momentum_minus") {
              counter_parameters_.momentum_minus = v.get<int>();
            }
          }

        } else if (key == "description" ||
                   key == "conditions" ||
                   key == "parameters" ||
                   key == "type") {
          // Do nothing

        } else {
          throw pqrs::json::unmarshal_error(fmt::format("unknown key `{0}` in `{1}`", key, json.dump()));
        }
      }

      counter_ = std::make_unique<counter>(weak_dispatcher_,
                                           counter_parameters_);

      counter_->scroll_event_arrived.connect([this](auto&& pointing_motion) {
        post_events(pointing_motion);
      });

    } catch (...) {
      detach_from_dispatcher([this] {
        counter_ = nullptr;
      });
      throw;
    }
  }

  virtual ~mouse_motion_to_scroll(void) {
    detach_from_dispatcher([this] {
      counter_ = nullptr;
    });
  }

  virtual bool already_manipulated(const event_queue::entry& front_input_event) {
    return false;
  }

  virtual manipulate_result manipulate(event_queue::entry& front_input_event,
                                       const event_queue::queue& input_event_queue,
                                       const std::shared_ptr<event_queue::queue>& output_event_queue,
                                       absolute_time_point now) {
    if (output_event_queue) {
      // ----------------------------------------

      if (!front_input_event.get_valid()) {
        return manipulate_result::passed;
      }

      if (!valid_) {
        return manipulate_result::passed;
      }

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

      auto from_mandatory_modifiers = test_conditions(front_input_event,
                                                      output_event_queue);
      if (!from_mandatory_modifiers) {
        counter_->async_reset();

      } else {
        if (auto m = front_input_event.get_event().find<pointing_motion>()) {
          front_input_event.set_valid(false);

          counter_->update(*m, front_input_event.get_event_time_stamp().get_time_stamp());

          from_mandatory_modifiers_ = *from_mandatory_modifiers;
          device_id_ = front_input_event.get_device_id();
          original_event_ = front_input_event.get_original_event();
          weak_output_event_queue_ = output_event_queue;

          return manipulate_result::manipulated;
        }
      }
    }

    return manipulate_result::passed;
  }

  virtual bool active(void) const {
    return false;
  }

  virtual bool needs_virtual_hid_pointing(void) const {
    return true;
  }

  virtual void handle_device_keys_and_pointing_buttons_are_released_event(const event_queue::entry& front_input_event,
                                                                          event_queue::queue& output_event_queue) {
  }

  virtual void handle_device_ungrabbed_event(device_id device_id,
                                             const event_queue::queue& output_event_queue,
                                             absolute_time_point time_stamp) {
  }

  virtual void handle_pointing_device_event_from_event_tap(const event_queue::entry& front_input_event,
                                                           event_queue::queue& output_event_queue) {
  }

private:
  std::optional<std::unordered_set<modifier_flag>> test_conditions(const event_queue::entry& front_input_event,
                                                                   const std::shared_ptr<event_queue::queue>& output_event_queue) const {
    if (!condition_manager_.is_fulfilled(front_input_event,
                                         output_event_queue->get_manipulator_environment())) {
      return std::nullopt;
    }

    return from_modifiers_definition_.test_modifiers(output_event_queue->get_modifier_flag_manager());
  }

  void post_events(const pointing_motion& pointing_motion) {
    if (auto output_event_queue = weak_output_event_queue_.lock()) {
      event_queue::event_time_stamp event_time_stamp(pqrs::osx::chrono::mach_absolute_time_point());
      absolute_time_duration time_stamp_delay(0);

      // Post from_mandatory_modifiers key_up

      base::post_lazy_modifier_key_events(from_mandatory_modifiers_,
                                          event_type::key_up,
                                          device_id_,
                                          event_time_stamp,
                                          time_stamp_delay,
                                          original_event_,
                                          *output_event_queue);

      // Post new event

      {
        auto t = event_time_stamp;
        t.set_time_stamp(t.get_time_stamp() + time_stamp_delay++);

        output_event_queue->emplace_back_entry(device_id_,
                                               t,
                                               event_queue::event(pointing_motion),
                                               event_type::single,
                                               original_event_);
      }

      // Post from_mandatory_modifiers key_down

      base::post_lazy_modifier_key_events(from_mandatory_modifiers_,
                                          event_type::key_down,
                                          device_id_,
                                          event_time_stamp,
                                          time_stamp_delay,
                                          original_event_,
                                          *output_event_queue);

      krbn_notification_center::get_instance().enqueue_input_event_arrived(*this);
    }
  }

  from_modifiers_definition from_modifiers_definition_;
  counter_parameters counter_parameters_;
  std::unique_ptr<counter> counter_;

  std::unordered_set<modifier_flag> from_mandatory_modifiers_;
  device_id device_id_;
  event_queue::event original_event_;
  std::weak_ptr<event_queue::queue> weak_output_event_queue_;
};
} // namespace mouse_motion_to_scroll
} // namespace manipulators
} // namespace manipulator
} // namespace krbn
<commit_msg>update error messages<commit_after>#pragma once

#include "../../types.hpp"
#include "../base.hpp"
#include "counter.hpp"
#include "krbn_notification_center.hpp"
#include <nlohmann/json.hpp>
#include <pqrs/dispatcher.hpp>

namespace krbn {
namespace manipulator {
namespace manipulators {
namespace mouse_motion_to_scroll {
class mouse_motion_to_scroll final : public base, public pqrs::dispatcher::extra::dispatcher_client {
public:
  mouse_motion_to_scroll(const nlohmann::json& json,
                         const core_configuration::details::complex_modifications_parameters& parameters) : base(),
                                                                                dispatcher_client() {
    try {
      if (!json.is_object()) {
        throw pqrs::json::unmarshal_error(fmt::format("json must be object, but is `{0}`", json.dump()));
      }

      for (const auto& [key, value] : json.items()) {
        if (key == "from") {
          if (!value.is_object()) {
            throw pqrs::json::unmarshal_error(fmt::format("`{0}` must be object, but is `{1}`", key, value.dump()));
          }

          for (const auto& [k, v] : value.items()) {
            if (k == "modifiers") {
              try {
                from_modifiers_definition_ = v.get<from_modifiers_definition>();
              } catch (const pqrs::json::unmarshal_error& e) {
                throw pqrs::json::unmarshal_error(fmt::format("`{0}.{1}` error: {2}", key, k, e.what()));
              }

            } else {
              throw pqrs::json::unmarshal_error(fmt::format("`{0}` error: unknown key `{1}` in `{2}`",
                                                            key,
                                                            k,
                                                            value.dump()));
            }
          }

        } else if (key == "options") {
          if (!value.is_object()) {
            throw pqrs::json::unmarshal_error(fmt::format("`{0}` must be object, but is `{1}`", key, value.dump()));
          }

          for (const auto& [k, v] : value.items()) {
            if (k == "threshold") { // (secret parameter)
              counter_parameters_.set_threshold(v.get<int>());
            } else if (k == "recent_time_duration") {
              counter_parameters_.recent_time_duration =
                  pqrs::osx::chrono::make_absolute_time_duration(
                      std::chrono::milliseconds(v.get<int>()));
            } else if (k == "value_scale") {
              counter_parameters_.value_scale = v.get<double>();
            } else if (k == "momentum_minus") {
              counter_parameters_.momentum_minus = v.get<int>();
            }
          }

        } else if (key == "description" ||
                   key == "conditions" ||
                   key == "parameters" ||
                   key == "type") {
          // Do nothing

        } else {
          throw pqrs::json::unmarshal_error(fmt::format("unknown key `{0}` in `{1}`", key, json.dump()));
        }
      }

      counter_ = std::make_unique<counter>(weak_dispatcher_,
                                           counter_parameters_);

      counter_->scroll_event_arrived.connect([this](auto&& pointing_motion) {
        post_events(pointing_motion);
      });

    } catch (...) {
      detach_from_dispatcher([this] {
        counter_ = nullptr;
      });
      throw;
    }
  }

  virtual ~mouse_motion_to_scroll(void) {
    detach_from_dispatcher([this] {
      counter_ = nullptr;
    });
  }

  virtual bool already_manipulated(const event_queue::entry& front_input_event) {
    return false;
  }

  virtual manipulate_result manipulate(event_queue::entry& front_input_event,
                                       const event_queue::queue& input_event_queue,
                                       const std::shared_ptr<event_queue::queue>& output_event_queue,
                                       absolute_time_point now) {
    if (output_event_queue) {
      // ----------------------------------------

      if (!front_input_event.get_valid()) {
        return manipulate_result::passed;
      }

      if (!valid_) {
        return manipulate_result::passed;
      }

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

      auto from_mandatory_modifiers = test_conditions(front_input_event,
                                                      output_event_queue);
      if (!from_mandatory_modifiers) {
        counter_->async_reset();

      } else {
        if (auto m = front_input_event.get_event().find<pointing_motion>()) {
          front_input_event.set_valid(false);

          counter_->update(*m, front_input_event.get_event_time_stamp().get_time_stamp());

          from_mandatory_modifiers_ = *from_mandatory_modifiers;
          device_id_ = front_input_event.get_device_id();
          original_event_ = front_input_event.get_original_event();
          weak_output_event_queue_ = output_event_queue;

          return manipulate_result::manipulated;
        }
      }
    }

    return manipulate_result::passed;
  }

  virtual bool active(void) const {
    return false;
  }

  virtual bool needs_virtual_hid_pointing(void) const {
    return true;
  }

  virtual void handle_device_keys_and_pointing_buttons_are_released_event(const event_queue::entry& front_input_event,
                                                                          event_queue::queue& output_event_queue) {
  }

  virtual void handle_device_ungrabbed_event(device_id device_id,
                                             const event_queue::queue& output_event_queue,
                                             absolute_time_point time_stamp) {
  }

  virtual void handle_pointing_device_event_from_event_tap(const event_queue::entry& front_input_event,
                                                           event_queue::queue& output_event_queue) {
  }

private:
  std::optional<std::unordered_set<modifier_flag>> test_conditions(const event_queue::entry& front_input_event,
                                                                   const std::shared_ptr<event_queue::queue>& output_event_queue) const {
    if (!condition_manager_.is_fulfilled(front_input_event,
                                         output_event_queue->get_manipulator_environment())) {
      return std::nullopt;
    }

    return from_modifiers_definition_.test_modifiers(output_event_queue->get_modifier_flag_manager());
  }

  void post_events(const pointing_motion& pointing_motion) {
    if (auto output_event_queue = weak_output_event_queue_.lock()) {
      event_queue::event_time_stamp event_time_stamp(pqrs::osx::chrono::mach_absolute_time_point());
      absolute_time_duration time_stamp_delay(0);

      // Post from_mandatory_modifiers key_up

      base::post_lazy_modifier_key_events(from_mandatory_modifiers_,
                                          event_type::key_up,
                                          device_id_,
                                          event_time_stamp,
                                          time_stamp_delay,
                                          original_event_,
                                          *output_event_queue);

      // Post new event

      {
        auto t = event_time_stamp;
        t.set_time_stamp(t.get_time_stamp() + time_stamp_delay++);

        output_event_queue->emplace_back_entry(device_id_,
                                               t,
                                               event_queue::event(pointing_motion),
                                               event_type::single,
                                               original_event_);
      }

      // Post from_mandatory_modifiers key_down

      base::post_lazy_modifier_key_events(from_mandatory_modifiers_,
                                          event_type::key_down,
                                          device_id_,
                                          event_time_stamp,
                                          time_stamp_delay,
                                          original_event_,
                                          *output_event_queue);

      krbn_notification_center::get_instance().enqueue_input_event_arrived(*this);
    }
  }

  from_modifiers_definition from_modifiers_definition_;
  counter_parameters counter_parameters_;
  std::unique_ptr<counter> counter_;

  std::unordered_set<modifier_flag> from_mandatory_modifiers_;
  device_id device_id_;
  event_queue::event original_event_;
  std::weak_ptr<event_queue::queue> weak_output_event_queue_;
};
} // namespace mouse_motion_to_scroll
} // namespace manipulators
} // namespace manipulator
} // namespace krbn
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (C) 2009 by Miguel Chavez Gamboa                            *
 *   miguel@lemonpos.org                                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include "mibitdialog.h"

#include <QPixmap>
#include <QString>
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QTimeLine>
#include <QPushButton>
#include <QDebug>

MibitDialog::MibitDialog( QWidget *parent, const QString &msg, const QString &file, const QPixmap &icon, AnimationType animation )
        : QSvgWidget( parent )
{
    if (file != 0) setSVG(file);

    m_parent = parent;
    animType = animation;
    setMinimumHeight(150);
    setFixedSize(0,0); //until show we grow it
    setMaxHeight(maxH); //default sizes
    setMaxWidth(maxW);
    animRate = 500; //default animation speed (half second rate).

    img      = new QLabel();
    hLayout   = new QHBoxLayout();
    vLayout  = new QVBoxLayout();
    text     = new QLabel(msg);
    btnClose = new QPushButton("Close"); ///TODO: what about translations???

    img->setPixmap(icon);
    img->setMaximumHeight(48); //the icon size is hardcoded now.
    img->setMaximumWidth(48);
    img->setAlignment(Qt::AlignLeft);

    btnClose->setMaximumWidth(120);
    btnClose->setShortcut(Qt::Key_Escape);

    setLayout(vLayout);
    text->setWordWrap(true);
    text->setMargin(5);

    hLayout->addWidget(img,0,Qt::AlignLeft);
    hLayout->addWidget(text,0,Qt::AlignLeft);
    vLayout->addLayout(hLayout,0);
    vLayout->addWidget(btnClose,0,Qt::AlignCenter);

    timeLine = new QTimeLine(animRate, this); //1 second animation, check later.
    connect(timeLine, SIGNAL(frameChanged(int)), this, SLOT(animate(int)));
    connect(btnClose,SIGNAL(clicked()),this, SLOT(hideDialog()));
    connect(timeLine,SIGNAL(finished()), this, SLOT(onAnimationFinished()));
}

MibitDialog::~MibitDialog()
{
}

void MibitDialog::showDialog(const QString &msg, AnimationType animation )
{
    //set default msg if the sent is empty. Also set the animation -the same way-.
    if (msg.isEmpty()) setMessage(text->text()); else setMessage( msg );
    if (animation == 0) setAnimationType( atSlideDown ); else setAnimationType( animation );

    show();
    //update steps for animation, now that the window is showing.
    int maxStep; int minStep = 0;
    if ( animType == atSlideDown ) {
        maxStep = (m_parent->geometry().height()/2)-(maxHeight/2);
        minStep = -maxHeight;
        //timeLine->setCurveShape(QTimeLine::EaseOutCurve); //QTimeLine::SineCurve: Hacerlo criticamente amortiguado
    } else if (animType == atSlideUp ) {
        maxStep = (m_parent->geometry().height()/2)-(maxHeight/2);
        minStep = maxHeight + m_parent->geometry().height();
    }
    else if ( animType == atGrowCenterH ) maxStep = maxWidth;
    else maxStep=maxHeight;

    timeLine->setFrameRange(minStep,maxStep);
    //make it grow
    timeLine->setDirection(QTimeLine::Forward);
    timeLine->start();

    btnClose->setFocus();
}

void MibitDialog::animate(int step)
{
    //get some sizes...
    int textW = text->width();
    int textH = text->height();
    int btnH = btnClose->height();
    QRect windowGeom = m_parent->geometry();
    int midPointX = (windowGeom.width()/2);
    int midPointY = (windowGeom.height()/2);
    int newY;
    int newX;
    if ((midPointX-(maxWidth/2)) < 0) newX = 0; else newX = midPointX - maxWidth/2;
    if ((midPointY-(maxHeight/2)) < 0) newY = 0; else newY = midPointY - maxHeight/2;

    QRect dRect;
    switch (animType) {
        case atGrowCenterV:   // Grow from Center Vertically.. to up and down
            break;
        case atGrowCenterH:   // Grow from Center Horizontally... from left to right
            break;
        case atSlideDown:  // slide Up
        case atSlideUp:    // Slide down
            dRect.setX(newX);
            dRect.setY(step);
            dRect.setWidth(maxWidth);
            dRect.setHeight(maxHeight);
            setGeometry(dRect);

            setFixedHeight(maxHeight);
            setFixedWidth(maxWidth);
            break;
        default:
           break;
    }
}

void MibitDialog::hideDialog()
{
    timeLine->toggleDirection();//reverse!
    timeLine->start();
}

void MibitDialog::onAnimationFinished()
{
    if (timeLine->direction() == QTimeLine::Backward) {
        close();
        //qDebug()<<"Animation finished and it was in backward direction";
    }
}

void MibitDialog::setSVG(const QString &file)
{
    load(file);
}

void MibitDialog::setIcon(const QPixmap &icon)
{
     img->setPixmap(icon);
 }

void MibitDialog::setMessage(const QString &msg)
{
    text->setText(msg);
}
<commit_msg>Completed all animation types<commit_after>/***************************************************************************
 *   Copyright (C) 2009 by Miguel Chavez Gamboa                            *
 *   miguel@lemonpos.org                                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include "mibitdialog.h"

#include <QPixmap>
#include <QString>
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QTimeLine>
#include <QPushButton>
#include <QDebug>

MibitDialog::MibitDialog( QWidget *parent, const QString &msg, const QString &file, const QPixmap &icon, AnimationType animation )
        : QSvgWidget( parent )
{
    if (file != 0) setSVG(file);

    m_parent = parent;
    animType = animation;
    setMinimumHeight(150);
    setFixedSize(0,0); //until show we grow it
    setMaxHeight(maxH); //default sizes
    setMaxWidth(maxW);
    animRate = 500; //default animation speed (half second rate).

    img      = new QLabel();
    hLayout   = new QHBoxLayout();
    vLayout  = new QVBoxLayout();
    text     = new QLabel(msg);
    btnClose = new QPushButton("Close"); ///TODO: what about translations???

    img->setPixmap(icon);
    img->setMaximumHeight(48); //the icon size is hardcoded now.
    img->setMaximumWidth(48);
    img->setAlignment(Qt::AlignLeft);

    btnClose->setMaximumWidth(120);
    btnClose->setShortcut(Qt::Key_Escape);

    setLayout(vLayout);
    text->setWordWrap(true);
    text->setMargin(5);

    hLayout->addWidget(img,0,Qt::AlignLeft);
    hLayout->addWidget(text,0,Qt::AlignLeft);
    vLayout->addLayout(hLayout,0);
    vLayout->addWidget(btnClose,0,Qt::AlignCenter);

    timeLine = new QTimeLine(animRate, this);
    connect(timeLine, SIGNAL(frameChanged(int)), this, SLOT(animate(int)));
    connect(btnClose,SIGNAL(clicked()),this, SLOT(hideDialog()));
    connect(timeLine,SIGNAL(finished()), this, SLOT(onAnimationFinished()));
}

MibitDialog::~MibitDialog()
{
}

void MibitDialog::showDialog(const QString &msg, AnimationType animation )
{
    //set default msg if the sent is empty. Also set the animation -the same way-.
    if (msg.isEmpty()) setMessage(text->text()); else setMessage( msg );
    if (animation == 0) setAnimationType( atSlideDown ); else setAnimationType( animation );

    show();
    //update steps for animation, now that the window is showing.

    int maxStep; int minStep = 0;
    switch (animType) {
        case atSlideDown:
            maxStep = (m_parent->geometry().height()/2)-(maxHeight/2);
            minStep = -maxHeight;
            break;
        case atSlideUp:
            maxStep = (m_parent->geometry().height()/2)-(maxHeight/2);
            minStep = maxHeight + m_parent->geometry().height();
            break;
        case atGrowCenterH:
            maxStep = maxWidth;
            minStep = 0;
            break;
        case atGrowCenterV:
            maxStep= maxHeight;
            minStep = 0;
            break;
    }

    //timeLine->setCurveShape(QTimeLine::EaseOutCurve); //QTimeLine::SineCurve: Hacerlo criticamente amortiguado
    timeLine->setFrameRange(minStep,maxStep);
    //make it grow
    timeLine->setDirection(QTimeLine::Forward);
    timeLine->start();
    btnClose->setFocus();
}

void MibitDialog::animate(int step)
{
    //get some sizes...
    int textW = text->width();
    int textH = text->height();
    int btnH = btnClose->height();
    QRect windowGeom = m_parent->geometry();
    int midPointX = (windowGeom.width()/2);
    int midPointY = (windowGeom.height()/2);
    int newY;
    int newX;
    //if ((midPointX-(maxWidth/2)) < 0) newX = 0; else newX = midPointX - maxWidth/2;
    //if ((midPointY-(maxHeight/2)) < 0) newY = 0; else newY = midPointY - maxHeight/2;

    QRect dRect;
    switch (animType) {
        case atGrowCenterV:   // Grow from Center Vertically.. to up and down
            if ((midPointY - step/2) < 0 ) newY = 0; else newY = midPointY - step/2;
            if ((midPointX-(maxWidth/2)) < 0) newX = 0; else newX = midPointX - maxWidth/2;
            dRect.setX(newX);
            dRect.setY(newY);
            dRect.setWidth(step);
            dRect.setHeight(maxHeight);
            setGeometry(dRect);
            setFixedHeight(step);
            setFixedWidth(maxWidth);
            break;
        case atGrowCenterH:   // Grow from Center Horizontally... from left to right
            if ((midPointX - step/2) < 0 ) newX = 0; else newX = midPointX - step/2;
            if ((midPointY-(maxHeight/2)) < 0) newY = 0; else newY = midPointY - maxHeight/2;
            dRect.setX(newX);
            dRect.setY(newY);
            dRect.setWidth(maxWidth);
            dRect.setHeight(step);
            setGeometry(dRect);
            setFixedHeight(maxHeight);
            setFixedWidth(step);
            break;
        case atSlideDown:  // slide Up
        case atSlideUp:    // Slide down
            if ((midPointX-(maxWidth/2)) < 0) newX = 0; else newX = midPointX - maxWidth/2;
            dRect.setX(newX);
            dRect.setY(step);
            dRect.setWidth(maxWidth);
            dRect.setHeight(maxHeight);
            setGeometry(dRect);

            setFixedHeight(maxHeight);
            setFixedWidth(maxWidth);
            break;
        default:
           break;
    }
}

void MibitDialog::hideDialog()
{
    timeLine->toggleDirection();//reverse!
    timeLine->start();
}

void MibitDialog::onAnimationFinished()
{
    if (timeLine->direction() == QTimeLine::Backward) {
        close();
    }
}

void MibitDialog::setSVG(const QString &file)
{
    load(file);
}

void MibitDialog::setIcon(const QPixmap &icon)
{
     img->setPixmap(icon);
 }

void MibitDialog::setMessage(const QString &msg)
{
    text->setText(msg);
}
<|endoftext|>


<commit_before>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXDE-Qt - a lightweight, Qt based, desktop toolset
 * http://razor-qt.org
 *
 * Copyright: 2012 Razor team
 * Authors:
 *   Johannes Zellner <webmaster@nebulon.de>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "pulseaudioengine.h"

#include "audiodevice.h"

#include <QMetaType>
#include <QtDebug>

//#define PULSEAUDIO_ENGINE_DEBUG

static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int isLast, void *userdata)
{
    PulseAudioEngine *pulseEngine = static_cast<PulseAudioEngine*>(userdata);
    QMap<pa_sink_state, QString> stateMap;
    stateMap[PA_SINK_INVALID_STATE] = "n/a";
    stateMap[PA_SINK_RUNNING] = "RUNNING";
    stateMap[PA_SINK_IDLE] = "IDLE";
    stateMap[PA_SINK_SUSPENDED] = "SUSPENDED";

    if (isLast < 0) {
        pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
        qWarning() << QString("Failed to get sink information: %1").arg(pa_strerror(pa_context_errno(context)));
        return;
    }

    if (isLast) {
        pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
        return;
    }

    pulseEngine->addOrUpdateSink(info);
}

static void contextEventCallback(pa_context *context, const char *name, pa_proplist *p, void *userdata)
{
#ifdef PULSEAUDIO_ENGINE_DEBUG
    qWarning("event received %s", name);
#endif
}

static void contextStateCallback(pa_context *context, void *userdata)
{
    PulseAudioEngine *pulseEngine = reinterpret_cast<PulseAudioEngine*>(userdata);

    // update internal state
    pa_context_state_t state = pa_context_get_state(context);
    pulseEngine->setContextState(state);

#ifdef PULSEAUDIO_ENGINE_DEBUG
    switch (state) {
        case PA_CONTEXT_UNCONNECTED:
            qWarning("context unconnected");
            break;
        case PA_CONTEXT_CONNECTING:
            qWarning("context connecting");
            break;
        case PA_CONTEXT_AUTHORIZING:
            qWarning("context authorizing");
            break;
        case PA_CONTEXT_SETTING_NAME:
            qWarning("context setting name");
            break;
        case PA_CONTEXT_READY:
            qWarning("context ready");
            break;
        case PA_CONTEXT_FAILED:
            qWarning("context failed");
            break;
        case PA_CONTEXT_TERMINATED:
            qWarning("context terminated");
            break;
        default:
            qWarning("we should never hit this state");
    }
#endif

    pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}

static void contextSuccessCallback(pa_context *context, int success, void *userdata)
{
    Q_UNUSED(context);
    Q_UNUSED(success);
    Q_UNUSED(userdata);

    PulseAudioEngine *pulseEngine = reinterpret_cast<PulseAudioEngine*>(userdata);
    pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}

static void contextSubscriptionCallback(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
{
    PulseAudioEngine *pulseEngine = reinterpret_cast<PulseAudioEngine*>(userdata);
    if (PA_SUBSCRIPTION_EVENT_REMOVE == t)
        pulseEngine->removeSink(idx);
    else
        pulseEngine->requestSinkInfoUpdate(idx);
}

PulseAudioEngine::PulseAudioEngine(QObject *parent) :
    AudioEngine(parent),
    m_context(0),
    m_contextState(PA_CONTEXT_UNCONNECTED),
    m_ready(false),
    m_maximumVolume(PA_VOLUME_UI_MAX)
{
    qRegisterMetaType<pa_context_state_t>("pa_context_state_t");

    m_reconnectionTimer.setSingleShot(true);
    m_reconnectionTimer.setInterval(100);
    connect(&m_reconnectionTimer, SIGNAL(timeout()), this, SLOT(connectContext()));

    m_mainLoop = pa_threaded_mainloop_new();
    if (m_mainLoop == 0) {
        qWarning("Unable to create pulseaudio mainloop");
        return;
    }

    if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
        qWarning("Unable to start pulseaudio mainloop");
        pa_threaded_mainloop_free(m_mainLoop);
        m_mainLoop = 0;
        return;
    }

    m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop);

    connect(this, SIGNAL(contextStateChanged(pa_context_state_t)), this, SLOT(handleContextStateChanged()));

    connectContext();
}

PulseAudioEngine::~PulseAudioEngine()
{
    if (m_context) {
        pa_context_unref(m_context);
        m_context = 0;
    }

    if (m_mainLoop) {
        pa_threaded_mainloop_free(m_mainLoop);
        m_mainLoop = 0;
    }
}

void PulseAudioEngine::removeSink(uint32_t idx)
{
    auto dev_i = std::find_if(m_sinks.begin(), m_sinks.end(), [idx] (AudioDevice * dev) { return dev->index() == idx; });
    if (m_sinks.end() == dev_i)
        return;

    QScopedPointer<AudioDevice> dev{*dev_i};
    m_cVolumeMap.remove(dev.data());
    m_sinks.erase(dev_i);
    emit sinkListChanged();
}

void PulseAudioEngine::addOrUpdateSink(const pa_sink_info *info)
{
    AudioDevice *dev = 0;
    bool newSink = false;
    QString name = QString::fromUtf8(info->name);

    foreach (AudioDevice *device, m_sinks) {
        if (device->name() == name) {
            dev = device;
            break;
        }
    }

    if (!dev) {
        dev = new AudioDevice(Sink, this);
        newSink = true;
    }

    dev->setName(name);
    dev->setIndex(info->index);
    dev->setDescription(QString::fromUtf8(info->description));
    dev->setMuteNoCommit(info->mute);

    // TODO: save separately? alsa does not have it
    m_cVolumeMap.insert(dev, info->volume);

    pa_volume_t v = pa_cvolume_avg(&(info->volume));
    // convert real volume to percentage
    dev->setVolumeNoCommit(((double)v * 100.0) / m_maximumVolume);

    if (newSink) {
        //keep the sinks sorted by index()
        m_sinks.insert(
                std::lower_bound(m_sinks.begin(), m_sinks.end(), dev,  [] (AudioDevice const * const a, AudioDevice const * const b) {
                    return a->name() < b->name();
                    })
                , dev
                );
        emit sinkListChanged();
    }
}

void PulseAudioEngine::requestSinkInfoUpdate(uint32_t idx)
{
    emit sinkInfoChanged(idx);
}

void PulseAudioEngine::commitDeviceVolume(AudioDevice *device)
{
    if (!device || !m_ready)
        return;

    // convert from percentage to real volume value
    pa_volume_t v = ((double)device->volume() / 100.0) * m_maximumVolume;
    pa_cvolume tmpVolume = m_cVolumeMap.value(device);
    pa_cvolume *volume = pa_cvolume_set(&tmpVolume, tmpVolume.channels, v);
    // qDebug() << "PulseAudioEngine::commitDeviceVolume" << v;
    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    if (device->type() == Sink)
        operation = pa_context_set_sink_volume_by_index(m_context, device->index(), volume, contextSuccessCallback, this);
    else
        operation = pa_context_set_source_volume_by_index(m_context, device->index(), volume, contextSuccessCallback, this);

    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::retrieveSinks()
{
    if (!m_ready)
        return;

    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    operation = pa_context_get_sink_info_list(m_context, sinkInfoCallback, this);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::setupSubscription()
{
    if (!m_ready)
        return;

    connect(this, &PulseAudioEngine::sinkInfoChanged, this, &PulseAudioEngine::retrieveSinkInfo, Qt::QueuedConnection);
    pa_context_set_subscribe_callback(m_context, contextSubscriptionCallback, this);

    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    operation = pa_context_subscribe(m_context, PA_SUBSCRIPTION_MASK_SINK, contextSuccessCallback, this);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::handleContextStateChanged()
{
    if (m_contextState == PA_CONTEXT_FAILED || m_contextState == PA_CONTEXT_TERMINATED) {
        qWarning("LXQt-Volume: Context connection failed or terminated lets try to reconnect");
        m_reconnectionTimer.start();
    }
}

void PulseAudioEngine::connectContext()
{
    bool keepGoing = true;
    bool ok = false;

    m_reconnectionTimer.stop();

    if (!m_mainLoop)
        return;

    pa_threaded_mainloop_lock(m_mainLoop);

    if (m_context) {
        pa_context_unref(m_context);
        m_context = 0;
    }

    m_context = pa_context_new(m_mainLoopApi, "lxqt-volume");
    pa_context_set_state_callback(m_context, contextStateCallback, this);
    pa_context_set_event_callback(m_context, contextEventCallback, this);

    if (!m_context) {
        pa_threaded_mainloop_unlock(m_mainLoop);
        m_reconnectionTimer.start();
        return;
    }

    if (pa_context_connect(m_context, NULL, (pa_context_flags_t)0, NULL) < 0) {
        pa_threaded_mainloop_unlock(m_mainLoop);
        m_reconnectionTimer.start();
        return;
    }

    while (keepGoing) {
        switch (m_contextState) {
            case PA_CONTEXT_CONNECTING:
            case PA_CONTEXT_AUTHORIZING:
            case PA_CONTEXT_SETTING_NAME:
                break;

            case PA_CONTEXT_READY:
                keepGoing = false;
                ok = true;
                break;

            case PA_CONTEXT_TERMINATED:
                keepGoing = false;
                break;

            case PA_CONTEXT_FAILED:
            default:
                qWarning() << QString("Connection failure: %1").arg(pa_strerror(pa_context_errno(m_context)));
                keepGoing = false;
        }

        if (keepGoing)
            pa_threaded_mainloop_wait(m_mainLoop);
    }

    pa_threaded_mainloop_unlock(m_mainLoop);

    if (ok) {
        retrieveSinks();
        setupSubscription();
    } else {
        m_reconnectionTimer.start();
    }
}

void PulseAudioEngine::retrieveSinkInfo(uint32_t idx)
{
    if (!m_ready)
        return;

    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    operation = pa_context_get_sink_info_by_index(m_context, idx, sinkInfoCallback, this);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::setMute(AudioDevice *device, bool state)
{
    if (!m_ready)
        return;

    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    operation = pa_context_set_sink_mute_by_index(m_context, device->index(), state, contextSuccessCallback, this);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::setContextState(pa_context_state_t state)
{
    if (m_contextState == state)
        return;

    m_contextState = state;

    // update ready member as it depends on state
    if (m_ready == (m_contextState == PA_CONTEXT_READY))
        return;

    m_ready = (m_contextState == PA_CONTEXT_READY);

    emit contextStateChanged(m_contextState);
    emit readyChanged(m_ready);
}

void PulseAudioEngine::setIgnoreMaxVolume(bool ignore)
{
    if (ignore)
        m_maximumVolume = PA_VOLUME_UI_MAX;
    else
        m_maximumVolume = pa_sw_volume_from_dB(0);
}

<commit_msg>volume: Fix reporting of pulse volume<commit_after>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXDE-Qt - a lightweight, Qt based, desktop toolset
 * http://razor-qt.org
 *
 * Copyright: 2012 Razor team
 * Authors:
 *   Johannes Zellner <webmaster@nebulon.de>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "pulseaudioengine.h"

#include "audiodevice.h"

#include <QMetaType>
#include <QtDebug>

//#define PULSEAUDIO_ENGINE_DEBUG

static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int isLast, void *userdata)
{
    PulseAudioEngine *pulseEngine = static_cast<PulseAudioEngine*>(userdata);
    QMap<pa_sink_state, QString> stateMap;
    stateMap[PA_SINK_INVALID_STATE] = "n/a";
    stateMap[PA_SINK_RUNNING] = "RUNNING";
    stateMap[PA_SINK_IDLE] = "IDLE";
    stateMap[PA_SINK_SUSPENDED] = "SUSPENDED";

    if (isLast < 0) {
        pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
        qWarning() << QString("Failed to get sink information: %1").arg(pa_strerror(pa_context_errno(context)));
        return;
    }

    if (isLast) {
        pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
        return;
    }

    pulseEngine->addOrUpdateSink(info);
}

static void contextEventCallback(pa_context *context, const char *name, pa_proplist *p, void *userdata)
{
#ifdef PULSEAUDIO_ENGINE_DEBUG
    qWarning("event received %s", name);
#endif
}

static void contextStateCallback(pa_context *context, void *userdata)
{
    PulseAudioEngine *pulseEngine = reinterpret_cast<PulseAudioEngine*>(userdata);

    // update internal state
    pa_context_state_t state = pa_context_get_state(context);
    pulseEngine->setContextState(state);

#ifdef PULSEAUDIO_ENGINE_DEBUG
    switch (state) {
        case PA_CONTEXT_UNCONNECTED:
            qWarning("context unconnected");
            break;
        case PA_CONTEXT_CONNECTING:
            qWarning("context connecting");
            break;
        case PA_CONTEXT_AUTHORIZING:
            qWarning("context authorizing");
            break;
        case PA_CONTEXT_SETTING_NAME:
            qWarning("context setting name");
            break;
        case PA_CONTEXT_READY:
            qWarning("context ready");
            break;
        case PA_CONTEXT_FAILED:
            qWarning("context failed");
            break;
        case PA_CONTEXT_TERMINATED:
            qWarning("context terminated");
            break;
        default:
            qWarning("we should never hit this state");
    }
#endif

    pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}

static void contextSuccessCallback(pa_context *context, int success, void *userdata)
{
    Q_UNUSED(context);
    Q_UNUSED(success);
    Q_UNUSED(userdata);

    PulseAudioEngine *pulseEngine = reinterpret_cast<PulseAudioEngine*>(userdata);
    pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}

static void contextSubscriptionCallback(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
{
    PulseAudioEngine *pulseEngine = reinterpret_cast<PulseAudioEngine*>(userdata);
    if (PA_SUBSCRIPTION_EVENT_REMOVE == t)
        pulseEngine->removeSink(idx);
    else
        pulseEngine->requestSinkInfoUpdate(idx);
}

PulseAudioEngine::PulseAudioEngine(QObject *parent) :
    AudioEngine(parent),
    m_context(0),
    m_contextState(PA_CONTEXT_UNCONNECTED),
    m_ready(false),
    m_maximumVolume(PA_VOLUME_UI_MAX)
{
    qRegisterMetaType<pa_context_state_t>("pa_context_state_t");

    m_reconnectionTimer.setSingleShot(true);
    m_reconnectionTimer.setInterval(100);
    connect(&m_reconnectionTimer, SIGNAL(timeout()), this, SLOT(connectContext()));

    m_mainLoop = pa_threaded_mainloop_new();
    if (m_mainLoop == 0) {
        qWarning("Unable to create pulseaudio mainloop");
        return;
    }

    if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
        qWarning("Unable to start pulseaudio mainloop");
        pa_threaded_mainloop_free(m_mainLoop);
        m_mainLoop = 0;
        return;
    }

    m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop);

    connect(this, SIGNAL(contextStateChanged(pa_context_state_t)), this, SLOT(handleContextStateChanged()));

    connectContext();
}

PulseAudioEngine::~PulseAudioEngine()
{
    if (m_context) {
        pa_context_unref(m_context);
        m_context = 0;
    }

    if (m_mainLoop) {
        pa_threaded_mainloop_free(m_mainLoop);
        m_mainLoop = 0;
    }
}

void PulseAudioEngine::removeSink(uint32_t idx)
{
    auto dev_i = std::find_if(m_sinks.begin(), m_sinks.end(), [idx] (AudioDevice * dev) { return dev->index() == idx; });
    if (m_sinks.end() == dev_i)
        return;

    QScopedPointer<AudioDevice> dev{*dev_i};
    m_cVolumeMap.remove(dev.data());
    m_sinks.erase(dev_i);
    emit sinkListChanged();
}

void PulseAudioEngine::addOrUpdateSink(const pa_sink_info *info)
{
    AudioDevice *dev = 0;
    bool newSink = false;
    QString name = QString::fromUtf8(info->name);

    foreach (AudioDevice *device, m_sinks) {
        if (device->name() == name) {
            dev = device;
            break;
        }
    }

    if (!dev) {
        dev = new AudioDevice(Sink, this);
        newSink = true;
    }

    dev->setName(name);
    dev->setIndex(info->index);
    dev->setDescription(QString::fromUtf8(info->description));
    dev->setMuteNoCommit(info->mute);

    // TODO: save separately? alsa does not have it
    m_cVolumeMap.insert(dev, info->volume);

    pa_volume_t v = pa_cvolume_avg(&(info->volume));
    // convert real volume to percentage
    dev->setVolumeNoCommit(qRound((static_cast<double>(v) * 100.0) / m_maximumVolume));

    if (newSink) {
        //keep the sinks sorted by index()
        m_sinks.insert(
                std::lower_bound(m_sinks.begin(), m_sinks.end(), dev,  [] (AudioDevice const * const a, AudioDevice const * const b) {
                    return a->name() < b->name();
                    })
                , dev
                );
        emit sinkListChanged();
    }
}

void PulseAudioEngine::requestSinkInfoUpdate(uint32_t idx)
{
    emit sinkInfoChanged(idx);
}

void PulseAudioEngine::commitDeviceVolume(AudioDevice *device)
{
    if (!device || !m_ready)
        return;

    // convert from percentage to real volume value
    pa_volume_t v = ((double)device->volume() / 100.0) * m_maximumVolume;
    pa_cvolume tmpVolume = m_cVolumeMap.value(device);
    pa_cvolume *volume = pa_cvolume_set(&tmpVolume, tmpVolume.channels, v);
    // qDebug() << "PulseAudioEngine::commitDeviceVolume" << v;
    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    if (device->type() == Sink)
        operation = pa_context_set_sink_volume_by_index(m_context, device->index(), volume, contextSuccessCallback, this);
    else
        operation = pa_context_set_source_volume_by_index(m_context, device->index(), volume, contextSuccessCallback, this);

    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::retrieveSinks()
{
    if (!m_ready)
        return;

    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    operation = pa_context_get_sink_info_list(m_context, sinkInfoCallback, this);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::setupSubscription()
{
    if (!m_ready)
        return;

    connect(this, &PulseAudioEngine::sinkInfoChanged, this, &PulseAudioEngine::retrieveSinkInfo, Qt::QueuedConnection);
    pa_context_set_subscribe_callback(m_context, contextSubscriptionCallback, this);

    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    operation = pa_context_subscribe(m_context, PA_SUBSCRIPTION_MASK_SINK, contextSuccessCallback, this);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::handleContextStateChanged()
{
    if (m_contextState == PA_CONTEXT_FAILED || m_contextState == PA_CONTEXT_TERMINATED) {
        qWarning("LXQt-Volume: Context connection failed or terminated lets try to reconnect");
        m_reconnectionTimer.start();
    }
}

void PulseAudioEngine::connectContext()
{
    bool keepGoing = true;
    bool ok = false;

    m_reconnectionTimer.stop();

    if (!m_mainLoop)
        return;

    pa_threaded_mainloop_lock(m_mainLoop);

    if (m_context) {
        pa_context_unref(m_context);
        m_context = 0;
    }

    m_context = pa_context_new(m_mainLoopApi, "lxqt-volume");
    pa_context_set_state_callback(m_context, contextStateCallback, this);
    pa_context_set_event_callback(m_context, contextEventCallback, this);

    if (!m_context) {
        pa_threaded_mainloop_unlock(m_mainLoop);
        m_reconnectionTimer.start();
        return;
    }

    if (pa_context_connect(m_context, NULL, (pa_context_flags_t)0, NULL) < 0) {
        pa_threaded_mainloop_unlock(m_mainLoop);
        m_reconnectionTimer.start();
        return;
    }

    while (keepGoing) {
        switch (m_contextState) {
            case PA_CONTEXT_CONNECTING:
            case PA_CONTEXT_AUTHORIZING:
            case PA_CONTEXT_SETTING_NAME:
                break;

            case PA_CONTEXT_READY:
                keepGoing = false;
                ok = true;
                break;

            case PA_CONTEXT_TERMINATED:
                keepGoing = false;
                break;

            case PA_CONTEXT_FAILED:
            default:
                qWarning() << QString("Connection failure: %1").arg(pa_strerror(pa_context_errno(m_context)));
                keepGoing = false;
        }

        if (keepGoing)
            pa_threaded_mainloop_wait(m_mainLoop);
    }

    pa_threaded_mainloop_unlock(m_mainLoop);

    if (ok) {
        retrieveSinks();
        setupSubscription();
    } else {
        m_reconnectionTimer.start();
    }
}

void PulseAudioEngine::retrieveSinkInfo(uint32_t idx)
{
    if (!m_ready)
        return;

    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    operation = pa_context_get_sink_info_by_index(m_context, idx, sinkInfoCallback, this);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::setMute(AudioDevice *device, bool state)
{
    if (!m_ready)
        return;

    pa_threaded_mainloop_lock(m_mainLoop);

    pa_operation *operation;
    operation = pa_context_set_sink_mute_by_index(m_context, device->index(), state, contextSuccessCallback, this);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
        pa_threaded_mainloop_wait(m_mainLoop);
    pa_operation_unref(operation);

    pa_threaded_mainloop_unlock(m_mainLoop);
}

void PulseAudioEngine::setContextState(pa_context_state_t state)
{
    if (m_contextState == state)
        return;

    m_contextState = state;

    // update ready member as it depends on state
    if (m_ready == (m_contextState == PA_CONTEXT_READY))
        return;

    m_ready = (m_contextState == PA_CONTEXT_READY);

    emit contextStateChanged(m_contextState);
    emit readyChanged(m_ready);
}

void PulseAudioEngine::setIgnoreMaxVolume(bool ignore)
{
    if (ignore)
        m_maximumVolume = PA_VOLUME_UI_MAX;
    else
        m_maximumVolume = pa_sw_volume_from_dB(0);
}

<|endoftext|>