<commit_before>/*
 * Copyright (c) 2011-2013, Ingo Weinhold <ingo_weinhold@gmx.de>
 *
 * This program is licensed under the BSD license, read LICENSE.BSD
 * for further information
 */

#include <package/PackageInfo.h>
#include <package/PackageInfoSet.h>
#include <package/PackageRoster.h>
#include <package/PackageVersion.h>
#include <package/RepositoryCache.h>
#include <package/RepositoryConfig.h>

#include "repo_haiku.h"

using namespace BPackageKit;
using namespace BPackageKit::BHPKG;

static BString haiku_version_to_string(const BPackageVersion &version)
{
  if (version.InitCheck() != B_OK)
    return BString();

  // compose ":<major>.<minor>.<micro>" (empty epoch)
  BString string(":");
  string << version.Major();
  if (!version.Minor().IsEmpty())
    {
      string << '.' << version.Minor();
      if (!version.Micro().IsEmpty())
        string << '.' << version.Micro();
    }

  // append pre-release
  if (!version.PreRelease().IsEmpty())
    string << '-' << version.PreRelease();

  // append revision
  if (version.Revision() != 0)
    string << '-' << version.Revision();

  return string;
}

static void add_dependency(Repo *repo, Offset &dependencies, const char *name,
  const char *version, int flags, const char* compatVersion = NULL)
{
  Pool *pool = repo->pool;

  Id dependency = pool_str2id(pool, name, 1);

  if (version && version[0] != '\0')
  {
    Id versionId = pool_str2id(pool, version, 1);

    if (compatVersion && compatVersion[0] != '\0')
      {
        versionId = pool_rel2id(pool, versionID, pool_str2id(pool, compatVersion, 1),
          REL_COMPAT, 1);
      }

    dependency = pool_rel2id(pool, dependency, versionId, flags, 1);
  }

  dependencies = repo_addid_dep(repo, dependencies, dependency, 0);
}

static void add_dependency(Repo *repo, Offset &dependencies, const char *name,
  const BPackageVersion &version, int flags)
{
  add_dependency(repo, dependencies, name, haiku_version_to_string(version),
    flags);
}

static void add_resolvables(Repo *repo, Offset &dependencies,
  const BObjectList<BPackageResolvable> &resolvables)
{
  for (int32 i = 0; BPackageResolvable *resolvable = resolvables.ItemAt(i); i++)
    {
      add_dependency(repo, dependencies, resolvable->Name(),
        haiku_version_to_string(resolvable->Version()), REL_EQ,
        haiku_version_to_string(resolvable->CompatibleVersion()));
    }
}

static void add_resolvable_expressions(Repo *repo, Offset &dependencies,
  const BObjectList<BPackageResolvableExpression> &expressions)
{
  for (int32 i = 0;
    BPackageResolvableExpression *expression = expressions.ItemAt(i); i++)
    {
      // It is possible that no version is specified. In that case any version
      // is acceptable.
      if (expression->Version().InitCheck() != B_OK)
        {
          BPackageVersion version;
          add_dependency(repo, dependencies, expression->Name(), NULL, 0);
          continue;
        }

      int flags = 0;
      switch (expression->Operator())
        {
          case B_PACKAGE_RESOLVABLE_OP_LESS:
            flags |= REL_LT;
            break;
          case B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL:
            flags |= REL_LT | REL_EQ;
            break;
          case B_PACKAGE_RESOLVABLE_OP_EQUAL:
            flags |= REL_EQ;
            break;
          case B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL:
            break;
          case B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL:
            flags |= REL_GT | REL_EQ;
            break;
          case B_PACKAGE_RESOLVABLE_OP_GREATER:
              flags |= REL_GT;
            break;
        }

      add_dependency(repo, dependencies, expression->Name(),
        expression->Version(), flags);
    }
}

static void add_replaces_list(Repo *repo, Offset &dependencies,
  const BStringList &packageNames)
{
  int32 count = packageNames.CountStrings();
  for (int32 i = 0; i < count; i++)
    {
      const BString &packageName = packageNames.StringAt(i);
      add_dependency(repo, dependencies, packageName, BPackageVersion(), 0);
    }
}

static Id add_package_info_to_repo(Repo *repo, Repodata *repoData,
  const BPackageInfo &packageInfo)
{
  Pool *pool = repo->pool;

  Id solvableId = repo_add_solvable(repo);
  Solvable *solvable = pool_id2solvable(pool, solvableId);
  // Prepend "pkg:" to package name, so "provides" don't match unless explicitly
  // specified this way.
  BString name("pkg:");
  name << packageInfo.Name();
  solvable->name = pool_str2id(pool, name, 1);
  if (packageInfo.Architecture() == B_PACKAGE_ARCHITECTURE_ANY)
    solvable->arch = ARCH_ANY;
  else
    solvable->arch = pool_str2id(pool,
      BPackageInfo::kArchitectureNames[packageInfo.Architecture()], 1);
  solvable->evr = pool_str2id(pool,
    haiku_version_to_string(packageInfo.Version()), 1);
  solvable->vendor = pool_str2id(pool, packageInfo.Vendor(), 1);
  repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_SUMMARY,
    packageInfo.Summary());
  repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_DESCRIPTION,
    packageInfo.Description());
  repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_PACKAGER,
    packageInfo.Packager());

  if (!packageInfo.Checksum().IsEmpty())
    repodata_set_checksum(repoData, solvable - pool->solvables,
      SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, packageInfo.Checksum());

  solvable->provides = repo_addid_dep(repo, solvable->provides,
    pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0);

  add_resolvables(repo, solvable->provides, packageInfo.ProvidesList());
  add_resolvable_expressions(repo, solvable->requires,
    packageInfo.RequiresList());
  add_resolvable_expressions(repo, solvable->supplements,
    packageInfo.SupplementsList());
  add_resolvable_expressions(repo, solvable->conflicts,
    packageInfo.ConflictsList());
  add_resolvable_expressions(repo, solvable->enhances,
    packageInfo.FreshensList());
  add_replaces_list(repo, solvable->obsoletes, packageInfo.ReplacesList());
  // TODO: Check whether freshens and replaces does indeed work as intended
  // here.

  // TODO: copyrights, licenses, URLs, source URLs

  return solvableId;
}

static void add_installed_packages(Repo *repo, Repodata *repoData,
  BPackageInstallationLocation location)
{
  BPackageRoster roster;
  BPackageInfoSet packageInfos;
  if (roster.GetActivePackages(location, packageInfos) == B_OK)
    {
      BRepositoryCache::Iterator it = packageInfos.GetIterator();
      while (const BPackageInfo *packageInfo = it.Next())
        add_package_info_to_repo(repo, repoData, *packageInfo);
    }
}

int repo_add_haiku_installed_packages(Repo *repo, const char *rootdir,
  int flags)
{
  Repodata *repoData = repo_add_repodata(repo, flags);

  add_installed_packages(repo, repoData,
    B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
  add_installed_packages(repo, repoData,
    B_PACKAGE_INSTALLATION_LOCATION_COMMON);
  add_installed_packages(repo, repoData, B_PACKAGE_INSTALLATION_LOCATION_HOME);

  if (!(flags & REPO_NO_INTERNALIZE))
    repodata_internalize(repoData);

  return 0;
}

Id repo_add_haiku_package(Repo *repo, const char *hpkgPath, int flags)
{
  BPackageInfo packageInfo;
  if (packageInfo.ReadFromPackageFile(hpkgPath) != B_OK)
    return 0;

  return repo_add_haiku_package_info(repo, packageInfo, flags);
}

int repo_add_haiku_packages(Repo *repo, const char *repoName, int flags)
{
  BPackageRoster roster;
  BRepositoryCache cache;
  if (roster.GetRepositoryCache(repoName, &cache) != B_OK)
    return 0;

  Repodata *repoData = repo_add_repodata(repo, flags);

  BRepositoryCache::Iterator it = cache.GetIterator();
  while (const BPackageInfo *packageInfo = it.Next())
    add_package_info_to_repo(repo, repoData, *packageInfo);

  if (!(flags & REPO_NO_INTERNALIZE))
    repodata_internalize(repoData);

  return 0;
}

Id repo_add_haiku_package_info(Repo *repo,
  const BPackageKit::BPackageInfo &packageInfo, int flags)
{
  if (packageInfo.InitCheck() != B_OK)
    return 0;

  Repodata *repoData = repo_add_repodata(repo, flags);

  Id id = add_package_info_to_repo(repo, repoData, packageInfo);

  if (!(flags & REPO_NO_INTERNALIZE))
    repodata_internalize(repoData);

  return id;
}
<commit_msg>repo_haiku: add_dependency(): Fix typo<commit_after>/*
 * Copyright (c) 2011-2013, Ingo Weinhold <ingo_weinhold@gmx.de>
 *
 * This program is licensed under the BSD license, read LICENSE.BSD
 * for further information
 */

#include <package/PackageInfo.h>
#include <package/PackageInfoSet.h>
#include <package/PackageRoster.h>
#include <package/PackageVersion.h>
#include <package/RepositoryCache.h>
#include <package/RepositoryConfig.h>

#include "repo_haiku.h"

using namespace BPackageKit;
using namespace BPackageKit::BHPKG;

static BString haiku_version_to_string(const BPackageVersion &version)
{
  if (version.InitCheck() != B_OK)
    return BString();

  // compose ":<major>.<minor>.<micro>" (empty epoch)
  BString string(":");
  string << version.Major();
  if (!version.Minor().IsEmpty())
    {
      string << '.' << version.Minor();
      if (!version.Micro().IsEmpty())
        string << '.' << version.Micro();
    }

  // append pre-release
  if (!version.PreRelease().IsEmpty())
    string << '-' << version.PreRelease();

  // append revision
  if (version.Revision() != 0)
    string << '-' << version.Revision();

  return string;
}

static void add_dependency(Repo *repo, Offset &dependencies, const char *name,
  const char *version, int flags, const char* compatVersion = NULL)
{
  Pool *pool = repo->pool;

  Id dependency = pool_str2id(pool, name, 1);

  if (version && version[0] != '\0')
  {
    Id versionId = pool_str2id(pool, version, 1);

    if (compatVersion && compatVersion[0] != '\0')
      {
        versionId = pool_rel2id(pool, versionId, pool_str2id(pool, compatVersion, 1),
          REL_COMPAT, 1);
      }

    dependency = pool_rel2id(pool, dependency, versionId, flags, 1);
  }

  dependencies = repo_addid_dep(repo, dependencies, dependency, 0);
}

static void add_dependency(Repo *repo, Offset &dependencies, const char *name,
  const BPackageVersion &version, int flags)
{
  add_dependency(repo, dependencies, name, haiku_version_to_string(version),
    flags);
}

static void add_resolvables(Repo *repo, Offset &dependencies,
  const BObjectList<BPackageResolvable> &resolvables)
{
  for (int32 i = 0; BPackageResolvable *resolvable = resolvables.ItemAt(i); i++)
    {
      add_dependency(repo, dependencies, resolvable->Name(),
        haiku_version_to_string(resolvable->Version()), REL_EQ,
        haiku_version_to_string(resolvable->CompatibleVersion()));
    }
}

static void add_resolvable_expressions(Repo *repo, Offset &dependencies,
  const BObjectList<BPackageResolvableExpression> &expressions)
{
  for (int32 i = 0;
    BPackageResolvableExpression *expression = expressions.ItemAt(i); i++)
    {
      // It is possible that no version is specified. In that case any version
      // is acceptable.
      if (expression->Version().InitCheck() != B_OK)
        {
          BPackageVersion version;
          add_dependency(repo, dependencies, expression->Name(), NULL, 0);
          continue;
        }

      int flags = 0;
      switch (expression->Operator())
        {
          case B_PACKAGE_RESOLVABLE_OP_LESS:
            flags |= REL_LT;
            break;
          case B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL:
            flags |= REL_LT | REL_EQ;
            break;
          case B_PACKAGE_RESOLVABLE_OP_EQUAL:
            flags |= REL_EQ;
            break;
          case B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL:
            break;
          case B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL:
            flags |= REL_GT | REL_EQ;
            break;
          case B_PACKAGE_RESOLVABLE_OP_GREATER:
              flags |= REL_GT;
            break;
        }

      add_dependency(repo, dependencies, expression->Name(),
        expression->Version(), flags);
    }
}

static void add_replaces_list(Repo *repo, Offset &dependencies,
  const BStringList &packageNames)
{
  int32 count = packageNames.CountStrings();
  for (int32 i = 0; i < count; i++)
    {
      const BString &packageName = packageNames.StringAt(i);
      add_dependency(repo, dependencies, packageName, BPackageVersion(), 0);
    }
}

static Id add_package_info_to_repo(Repo *repo, Repodata *repoData,
  const BPackageInfo &packageInfo)
{
  Pool *pool = repo->pool;

  Id solvableId = repo_add_solvable(repo);
  Solvable *solvable = pool_id2solvable(pool, solvableId);
  // Prepend "pkg:" to package name, so "provides" don't match unless explicitly
  // specified this way.
  BString name("pkg:");
  name << packageInfo.Name();
  solvable->name = pool_str2id(pool, name, 1);
  if (packageInfo.Architecture() == B_PACKAGE_ARCHITECTURE_ANY)
    solvable->arch = ARCH_ANY;
  else
    solvable->arch = pool_str2id(pool,
      BPackageInfo::kArchitectureNames[packageInfo.Architecture()], 1);
  solvable->evr = pool_str2id(pool,
    haiku_version_to_string(packageInfo.Version()), 1);
  solvable->vendor = pool_str2id(pool, packageInfo.Vendor(), 1);
  repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_SUMMARY,
    packageInfo.Summary());
  repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_DESCRIPTION,
    packageInfo.Description());
  repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_PACKAGER,
    packageInfo.Packager());

  if (!packageInfo.Checksum().IsEmpty())
    repodata_set_checksum(repoData, solvable - pool->solvables,
      SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, packageInfo.Checksum());

  solvable->provides = repo_addid_dep(repo, solvable->provides,
    pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0);

  add_resolvables(repo, solvable->provides, packageInfo.ProvidesList());
  add_resolvable_expressions(repo, solvable->requires,
    packageInfo.RequiresList());
  add_resolvable_expressions(repo, solvable->supplements,
    packageInfo.SupplementsList());
  add_resolvable_expressions(repo, solvable->conflicts,
    packageInfo.ConflictsList());
  add_resolvable_expressions(repo, solvable->enhances,
    packageInfo.FreshensList());
  add_replaces_list(repo, solvable->obsoletes, packageInfo.ReplacesList());
  // TODO: Check whether freshens and replaces does indeed work as intended
  // here.

  // TODO: copyrights, licenses, URLs, source URLs

  return solvableId;
}

static void add_installed_packages(Repo *repo, Repodata *repoData,
  BPackageInstallationLocation location)
{
  BPackageRoster roster;
  BPackageInfoSet packageInfos;
  if (roster.GetActivePackages(location, packageInfos) == B_OK)
    {
      BRepositoryCache::Iterator it = packageInfos.GetIterator();
      while (const BPackageInfo *packageInfo = it.Next())
        add_package_info_to_repo(repo, repoData, *packageInfo);
    }
}

int repo_add_haiku_installed_packages(Repo *repo, const char *rootdir,
  int flags)
{
  Repodata *repoData = repo_add_repodata(repo, flags);

  add_installed_packages(repo, repoData,
    B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
  add_installed_packages(repo, repoData,
    B_PACKAGE_INSTALLATION_LOCATION_COMMON);
  add_installed_packages(repo, repoData, B_PACKAGE_INSTALLATION_LOCATION_HOME);

  if (!(flags & REPO_NO_INTERNALIZE))
    repodata_internalize(repoData);

  return 0;
}

Id repo_add_haiku_package(Repo *repo, const char *hpkgPath, int flags)
{
  BPackageInfo packageInfo;
  if (packageInfo.ReadFromPackageFile(hpkgPath) != B_OK)
    return 0;

  return repo_add_haiku_package_info(repo, packageInfo, flags);
}

int repo_add_haiku_packages(Repo *repo, const char *repoName, int flags)
{
  BPackageRoster roster;
  BRepositoryCache cache;
  if (roster.GetRepositoryCache(repoName, &cache) != B_OK)
    return 0;

  Repodata *repoData = repo_add_repodata(repo, flags);

  BRepositoryCache::Iterator it = cache.GetIterator();
  while (const BPackageInfo *packageInfo = it.Next())
    add_package_info_to_repo(repo, repoData, *packageInfo);

  if (!(flags & REPO_NO_INTERNALIZE))
    repodata_internalize(repoData);

  return 0;
}

Id repo_add_haiku_package_info(Repo *repo,
  const BPackageKit::BPackageInfo &packageInfo, int flags)
{
  if (packageInfo.InitCheck() != B_OK)
    return 0;

  Repodata *repoData = repo_add_repodata(repo, flags);

  Id id = add_package_info_to_repo(repo, repoData, packageInfo);

  if (!(flags & REPO_NO_INTERNALIZE))
    repodata_internalize(repoData);

  return id;
}
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "helpwindow.h"
#include "utils.h"

#include "ui_mainwindow.h"

#ifdef Q_WS_WIN
    #include <windows.h>
#endif

#include <QApplication>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDir>
#include <QFileInfo>
#include <QHelpEngine>
#include <QMessageBox>
#include <QSettings>
#include <QTemporaryFile>
#include <QUrl>

#define OPENCOR_HOMEPAGE "http://opencor.sourceforge.net/"

#define SETTINGS_INSTITUTION "World"
#define SETTINGS_GENERAL_LOCALE "General_Locale"
#define SETTINGS_GENERAL_GEOMETRY "General_Geometry"
#define SETTINGS_GENERAL_STATE "General_State"
#define SETTINGS_HELPWINDOW_TEXTSIZEMULTIPLIER "HelpWindow_TextSizeMultiplier"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    // Retrieve the name of the operating system

    osName = getOsName();

    // Retrieve the name of the application

    appName = QFileInfo(qApp->applicationFilePath()).baseName();
    // Note: normally, one would probably use something like:
    //
    //           appName = qApp->applicationName();
    //
    //       but this will return an empty string (probably as a result of our
    //       use of CMake as opposed to QMake and therefore a .pro file), so...

    // Retrieve the version of the application

    QFile versionFile(":version");

    versionFile.open(QIODevice::ReadOnly);

    appVersion = QString(versionFile.readLine()).trimmed();

    if (appVersion.endsWith(".0"))
        // There is no actual patch information, so trim it

        appVersion.truncate(appVersion.length()-2);

    versionFile.close();

    // Set up the GUI

    ui->setupUi(this);

    // Extract the help files

    QTemporaryFile tempDir;

    tempDir.open();    // Note: this is required to get a 'valid' temporary
    tempDir.close();   //       directory name...

    tempDirName = tempDir.fileName().append(".dir");

    QDir().mkdir(tempDirName);

    QString applicationBaseName(QFileInfo(qApp->applicationFilePath()).baseName());

    qchFileName = tempDirName+QDir::separator()+applicationBaseName+".qch";
    qhcFileName = tempDirName+QDir::separator()+applicationBaseName+".qhc";

    saveResourceAs(":qchFile", qchFileName);
    saveResourceAs(":qhcFile", qhcFileName);

    // Set up the help engine

    helpEngine = new QHelpEngine(qhcFileName);

    helpEngine->setupData();

    // Help window

    helpWindow = new HelpWindow(helpEngine, QUrl("qthelp://world.opencor/doc/index.html"));

    connect(helpWindow, SIGNAL(visibilityChanged(bool)), this, SLOT(showHideHelp(bool)));

    // Default user settings

    on_actionResetAll_triggered();

    // Retrieve our default settings

    loadSettings();

    // Set the name of the main window to that of the application

    setWindowTitle(appName);
}

MainWindow::~MainWindow()
{
    // Keep track of our default settings

    saveSettings();

    // Delete some internal objects

    delete helpEngine;
    delete ui;

    // Delete the help files

    QFile(qchFileName).remove();
    QFile(qhcFileName).remove();

    QDir().rmdir(tempDirName);
}

void MainWindow::singleAppMsgRcvd(const QString&)
{
    // We have just received a message from another instance of OpenCOR, so
    // bring ourselves to the foreground
    // Note: one would normally use activateWindow(), but depending on the
    //       operating system it may or not bring OpenCOR to the foreground,
    //       so... instead we do what follows, depending on the operating
    //       system...

#ifdef Q_WS_WIN
    // Retrieve OpenCOR's window Id

    WId mwWinId = winId();

    // Restore OpenCOR, should it be minimized

    if (IsIconic(mwWinId))
        SendMessage(mwWinId, WM_SYSCOMMAND, SC_RESTORE, 0);

    // Bring OpenCOR to the foreground

    DWORD foregroundThreadPId = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
    DWORD mwThreadPId         = GetWindowThreadProcessId(mwWinId, NULL);

    if (foregroundThreadPId != mwThreadPId)
    {
        // OpenCOR's thread process Id is not that of the foreground window, so
        // attach the foreground thread to OpenCOR's, set OpenCOR to the
        // foreground, and detach the foreground thread from OpenCOR's

        AttachThreadInput(foregroundThreadPId, mwThreadPId, true);

        SetForegroundWindow(mwWinId);

        AttachThreadInput(foregroundThreadPId, mwThreadPId, false);
    }
    else
        // OpenCOR's thread process Id is that of the foreground window, so
        // just set OpenCOR to the foreground

        SetForegroundWindow(mwWinId);

    // Note: under Windows, to use activateWindow() will only highlight the
    //       application in the taskbar, since under Windows no application
    //       should be allowed to bring itself to the foreground when another
    //       application is already in the foreground. Fair enough, but it
    //       happens that, here, the user wants OpenCOR to be brought to the
    //       foreground, hence the above code to get the effect we are after...
#else
    // Do what one should normally do and which works fine under Mac OS X

    activateWindow();

    #ifdef Q_WS_X11
        raise();
        // Note: under Linux, to use activateWindow() may or not give the
        //       expected result. The above code is supposed to make sure that
        //       OpenCOR gets brought to the foreground, but that itsn't the
        //       case under Ubuntu at least (it works when you want to open a
        //       second instance of OpenCOR, but not thereafter!)...
    #endif
#endif

    // Now, we must handle the arguments that were passed to us

    // TODO: handle the arguments passed to the 'official' instance of OpenCOR
}

void MainWindow::loadSettings()
{
    QSettings settings(SETTINGS_INSTITUTION, appName);

    // Retrieve the language to be used by OpenCOR, with a default just in case

    setLocale(settings.value(SETTINGS_GENERAL_LOCALE, QLocale::system().name()).toString());

    // Retrieve the geometry of the main window

    restoreGeometry(settings.value(SETTINGS_GENERAL_GEOMETRY).toByteArray());

    // Retrieve the state of the main window

    restoreState(settings.value(SETTINGS_GENERAL_STATE).toByteArray());

    // Retrieve the text size multiplier for the help widget, with a default
    // value just in case

    helpWindow->setHelpWidgetTextSizeMultiplier(settings.value(SETTINGS_HELPWINDOW_TEXTSIZEMULTIPLIER, 1.0).toDouble());

#ifdef Q_WS_X11
    // Really hide the help window, if it is to be hidden upon starting OpenCOR
    // Indeed, on Linux, we still get some kind of a blank window, so...

    if (helpWindow->isHidden())
    {
        helpWindow->show();
        helpWindow->hide();
    }
#endif
}

void MainWindow::saveSettings()
{
    QSettings settings(SETTINGS_INSTITUTION, appName);

    // Keep track of the language to be used by OpenCOR

    settings.setValue(SETTINGS_GENERAL_LOCALE, locale);

    // Keep track of the geometry of the main window

    settings.setValue(SETTINGS_GENERAL_GEOMETRY, saveGeometry());

    // Keep track of the state of the main window

    settings.setValue(SETTINGS_GENERAL_STATE, saveState());

    // Keep track of the text size multiplier for the help widget

    settings.setValue(SETTINGS_HELPWINDOW_TEXTSIZEMULTIPLIER, helpWindow->helpWidgetTextSizeMultiplier());
}

void MainWindow::setLocale(const QString& newLocale)
{
    if (newLocale != locale)
    {
        locale = newLocale;

        // Specify the language to be used by OpenCOR

//        qApp->removeTranslator(qtTranslator);
//        qtTranslator->load("qt_"+newLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
//        qApp->installTranslator(qtTranslator);

//        qApp->removeTranslator(appTranslator);
//        appTranslator->load(":app_"+newLocale);
//        qApp->installTranslator(appTranslator);
    }

    // Update the checked menu item
    // Note: it has to be done every single time, since selecting a menu item
    //       will automatically toggle its checked status, so...

    ui->actionEnglish->setChecked(newLocale.startsWith("en"));
    ui->actionFrench->setChecked(newLocale.startsWith("fr"));
}

void MainWindow::notYetImplemented(const QString& message)
{
    // Display a warning message about a particular feature having not yet been
    // implemented

    QMessageBox::warning(this, appName, message+tr(" has not yet been implemented..."),
                         QMessageBox::Ok, QMessageBox::Ok);
}

void MainWindow::on_actionExit_triggered()
{
    // Exit OpenCOR

    close();
}

void MainWindow::on_actionEnglish_triggered()
{
    // Select English as the language used by OpenCOR

    setLocale("en");
}

void MainWindow::on_actionFrench_triggered()
{
    // Select French as the language used by OpenCOR

    setLocale("fr");
}

void MainWindow::showHideHelp(const bool& show)
{
    if (show)
        helpWindow->show();
    else
        helpWindow->hide();

    ui->actionHelp->setChecked(show);
}

void MainWindow::on_actionHelp_triggered()
{
    showHideHelp(ui->actionHelp->isChecked());
}

void MainWindow::on_actionHomepage_triggered()
{
    // Look up the OpenCOR home page

    QDesktopServices::openUrl(QUrl(OPENCOR_HOMEPAGE));
}

void MainWindow::on_actionAbout_triggered()
{
    QMessageBox::about(this, appName,
                       QString("")+
                       ""+
                           "
"+appName+" "+appVersion+"
"+
                           "
"+osName+"
"+
                       ""+
                       "
"+
                       ""+appName+" "+tr("is a cross-platform CellML-based modelling environment which can be used to organise, edit, simulate and analyse CellML files.")+"

"+
                       appName+" "+tr("is written in C++, using the Qt framework, and is not currently released under any particular license, but this is going to change in the future."));
}

void MainWindow::on_actionResetAll_triggered()
{
    // Default size and position of both the main and help windows

    const double mainRatio = 3.0/5.0;
    const double helpRatio = 1.0/3.0;
    const double spaceRatio = 1.0/45.0;
    const double horizSpace = spaceRatio*qApp->desktop()->width();
    const double vertSpace  = 2.0*spaceRatio*qApp->desktop()->height();

    addDockWidget(Qt::RightDockWidgetArea, helpWindow);
    // Note: the above is only required so that the help window can then be
    //       docked to the main window, should the user want to do that.
    //       Indeed, to make the help window float is not sufficient, so...

    helpWindow->setFloating(true);

    resize(QSize(mainRatio*qApp->desktop()->width(), mainRatio*qApp->desktop()->height()));
    helpWindow->resize(helpRatio*qApp->desktop()->width(), size().height());

    move(QPoint(horizSpace, vertSpace));
    helpWindow->move(QPoint(qApp->desktop()->width()-helpWindow->size().width()-horizSpace, vertSpace));

    helpWindow->hide();   // By default

    // Default language to be used by OpenCOR

    setLocale(QLocale::system().name());

    // Default text size multiplier for the help widget

    helpWindow->setHelpWidgetTextSizeMultiplier(1.0);
}
<commit_msg>Hide the help window straightaway before setting it up, thus avoiding that it shows up for a microsecond (so to speak!).<commit_after>#include "mainwindow.h"
#include "helpwindow.h"
#include "utils.h"

#include "ui_mainwindow.h"

#ifdef Q_WS_WIN
    #include <windows.h>
#endif

#include <QApplication>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDir>
#include <QFileInfo>
#include <QHelpEngine>
#include <QMessageBox>
#include <QSettings>
#include <QTemporaryFile>
#include <QUrl>

#define OPENCOR_HOMEPAGE "http://opencor.sourceforge.net/"

#define SETTINGS_INSTITUTION "World"
#define SETTINGS_GENERAL_LOCALE "General_Locale"
#define SETTINGS_GENERAL_GEOMETRY "General_Geometry"
#define SETTINGS_GENERAL_STATE "General_State"
#define SETTINGS_HELPWINDOW_TEXTSIZEMULTIPLIER "HelpWindow_TextSizeMultiplier"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    // Retrieve the name of the operating system

    osName = getOsName();

    // Retrieve the name of the application

    appName = QFileInfo(qApp->applicationFilePath()).baseName();
    // Note: normally, one would probably use something like:
    //
    //           appName = qApp->applicationName();
    //
    //       but this will return an empty string (probably as a result of our
    //       use of CMake as opposed to QMake and therefore a .pro file), so...

    // Retrieve the version of the application

    QFile versionFile(":version");

    versionFile.open(QIODevice::ReadOnly);

    appVersion = QString(versionFile.readLine()).trimmed();

    if (appVersion.endsWith(".0"))
        // There is no actual patch information, so trim it

        appVersion.truncate(appVersion.length()-2);

    versionFile.close();

    // Set up the GUI

    ui->setupUi(this);

    // Extract the help files

    QTemporaryFile tempDir;

    tempDir.open();    // Note: this is required to get a 'valid' temporary
    tempDir.close();   //       directory name...

    tempDirName = tempDir.fileName().append(".dir");

    QDir().mkdir(tempDirName);

    QString applicationBaseName(QFileInfo(qApp->applicationFilePath()).baseName());

    qchFileName = tempDirName+QDir::separator()+applicationBaseName+".qch";
    qhcFileName = tempDirName+QDir::separator()+applicationBaseName+".qhc";

    saveResourceAs(":qchFile", qchFileName);
    saveResourceAs(":qhcFile", qhcFileName);

    // Set up the help engine

    helpEngine = new QHelpEngine(qhcFileName);

    helpEngine->setupData();

    // Help window

    helpWindow = new HelpWindow(helpEngine, QUrl("qthelp://world.opencor/doc/index.html"));

    connect(helpWindow, SIGNAL(visibilityChanged(bool)), this, SLOT(showHideHelp(bool)));

    // Default user settings

    on_actionResetAll_triggered();

    // Retrieve our default settings

    loadSettings();

    // Set the name of the main window to that of the application

    setWindowTitle(appName);
}

MainWindow::~MainWindow()
{
    // Keep track of our default settings

    saveSettings();

    // Delete some internal objects

    delete helpEngine;
    delete ui;

    // Delete the help files

    QFile(qchFileName).remove();
    QFile(qhcFileName).remove();

    QDir().rmdir(tempDirName);
}

void MainWindow::singleAppMsgRcvd(const QString&)
{
    // We have just received a message from another instance of OpenCOR, so
    // bring ourselves to the foreground
    // Note: one would normally use activateWindow(), but depending on the
    //       operating system it may or not bring OpenCOR to the foreground,
    //       so... instead we do what follows, depending on the operating
    //       system...

#ifdef Q_WS_WIN
    // Retrieve OpenCOR's window Id

    WId mwWinId = winId();

    // Restore OpenCOR, should it be minimized

    if (IsIconic(mwWinId))
        SendMessage(mwWinId, WM_SYSCOMMAND, SC_RESTORE, 0);

    // Bring OpenCOR to the foreground

    DWORD foregroundThreadPId = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
    DWORD mwThreadPId         = GetWindowThreadProcessId(mwWinId, NULL);

    if (foregroundThreadPId != mwThreadPId)
    {
        // OpenCOR's thread process Id is not that of the foreground window, so
        // attach the foreground thread to OpenCOR's, set OpenCOR to the
        // foreground, and detach the foreground thread from OpenCOR's

        AttachThreadInput(foregroundThreadPId, mwThreadPId, true);

        SetForegroundWindow(mwWinId);

        AttachThreadInput(foregroundThreadPId, mwThreadPId, false);
    }
    else
        // OpenCOR's thread process Id is that of the foreground window, so
        // just set OpenCOR to the foreground

        SetForegroundWindow(mwWinId);

    // Note: under Windows, to use activateWindow() will only highlight the
    //       application in the taskbar, since under Windows no application
    //       should be allowed to bring itself to the foreground when another
    //       application is already in the foreground. Fair enough, but it
    //       happens that, here, the user wants OpenCOR to be brought to the
    //       foreground, hence the above code to get the effect we are after...
#else
    // Do what one should normally do and which works fine under Mac OS X

    activateWindow();

    #ifdef Q_WS_X11
        raise();
        // Note: under Linux, to use activateWindow() may or not give the
        //       expected result. The above code is supposed to make sure that
        //       OpenCOR gets brought to the foreground, but that itsn't the
        //       case under Ubuntu at least (it works when you want to open a
        //       second instance of OpenCOR, but not thereafter!)...
    #endif
#endif

    // Now, we must handle the arguments that were passed to us

    // TODO: handle the arguments passed to the 'official' instance of OpenCOR
}

void MainWindow::loadSettings()
{
    QSettings settings(SETTINGS_INSTITUTION, appName);

    // Retrieve the language to be used by OpenCOR, with a default just in case

    setLocale(settings.value(SETTINGS_GENERAL_LOCALE, QLocale::system().name()).toString());

    // Retrieve the geometry of the main window

    restoreGeometry(settings.value(SETTINGS_GENERAL_GEOMETRY).toByteArray());

    // Retrieve the state of the main window

    restoreState(settings.value(SETTINGS_GENERAL_STATE).toByteArray());

    // Retrieve the text size multiplier for the help widget, with a default
    // value just in case

    helpWindow->setHelpWidgetTextSizeMultiplier(settings.value(SETTINGS_HELPWINDOW_TEXTSIZEMULTIPLIER, 1.0).toDouble());

#ifdef Q_WS_X11
    // Really hide the help window, if it is to be hidden upon starting OpenCOR
    // Indeed, on Linux, we still get some kind of a blank window, so...

    if (helpWindow->isHidden())
    {
        helpWindow->show();
        helpWindow->hide();
    }
#endif
}

void MainWindow::saveSettings()
{
    QSettings settings(SETTINGS_INSTITUTION, appName);

    // Keep track of the language to be used by OpenCOR

    settings.setValue(SETTINGS_GENERAL_LOCALE, locale);

    // Keep track of the geometry of the main window

    settings.setValue(SETTINGS_GENERAL_GEOMETRY, saveGeometry());

    // Keep track of the state of the main window

    settings.setValue(SETTINGS_GENERAL_STATE, saveState());

    // Keep track of the text size multiplier for the help widget

    settings.setValue(SETTINGS_HELPWINDOW_TEXTSIZEMULTIPLIER, helpWindow->helpWidgetTextSizeMultiplier());
}

void MainWindow::setLocale(const QString& newLocale)
{
    if (newLocale != locale)
    {
        locale = newLocale;

        // Specify the language to be used by OpenCOR

//        qApp->removeTranslator(qtTranslator);
//        qtTranslator->load("qt_"+newLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
//        qApp->installTranslator(qtTranslator);

//        qApp->removeTranslator(appTranslator);
//        appTranslator->load(":app_"+newLocale);
//        qApp->installTranslator(appTranslator);
    }

    // Update the checked menu item
    // Note: it has to be done every single time, since selecting a menu item
    //       will automatically toggle its checked status, so...

    ui->actionEnglish->setChecked(newLocale.startsWith("en"));
    ui->actionFrench->setChecked(newLocale.startsWith("fr"));
}

void MainWindow::notYetImplemented(const QString& message)
{
    // Display a warning message about a particular feature having not yet been
    // implemented

    QMessageBox::warning(this, appName, message+tr(" has not yet been implemented..."),
                         QMessageBox::Ok, QMessageBox::Ok);
}

void MainWindow::on_actionExit_triggered()
{
    // Exit OpenCOR

    close();
}

void MainWindow::on_actionEnglish_triggered()
{
    // Select English as the language used by OpenCOR

    setLocale("en");
}

void MainWindow::on_actionFrench_triggered()
{
    // Select French as the language used by OpenCOR

    setLocale("fr");
}

void MainWindow::showHideHelp(const bool& show)
{
    if (show)
        helpWindow->show();
    else
        helpWindow->hide();

    ui->actionHelp->setChecked(show);
}

void MainWindow::on_actionHelp_triggered()
{
    showHideHelp(ui->actionHelp->isChecked());
}

void MainWindow::on_actionHomepage_triggered()
{
    // Look up the OpenCOR home page

    QDesktopServices::openUrl(QUrl(OPENCOR_HOMEPAGE));
}

void MainWindow::on_actionAbout_triggered()
{
    QMessageBox::about(this, appName,
                       QString("")+
                       ""+
                           "
"+appName+" "+appVersion+"
"+
                           "
"+osName+"
"+
                       ""+
                       "
"+
                       ""+appName+" "+tr("is a cross-platform CellML-based modelling environment which can be used to organise, edit, simulate and analyse CellML files.")+"

"+
                       appName+" "+tr("is written in C++, using the Qt framework, and is not currently released under any particular license, but this is going to change in the future."));
}

void MainWindow::on_actionResetAll_triggered()
{
    // Default size and position of both the main and help windows

    const double mainRatio = 3.0/5.0;
    const double helpRatio = 1.0/3.0;
    const double spaceRatio = 1.0/45.0;
    const double horizSpace = spaceRatio*qApp->desktop()->width();
    const double vertSpace  = 2.0*spaceRatio*qApp->desktop()->height();

    helpWindow->hide();   // By default

    addDockWidget(Qt::RightDockWidgetArea, helpWindow);
    // Note: the above is only required so that the help window can then be
    //       docked to the main window, should the user want to do that.
    //       Indeed, to make the help window float is not sufficient, so...

    helpWindow->setFloating(true);

    resize(QSize(mainRatio*qApp->desktop()->width(), mainRatio*qApp->desktop()->height()));
    helpWindow->resize(helpRatio*qApp->desktop()->width(), size().height());

    move(QPoint(horizSpace, vertSpace));
    helpWindow->move(QPoint(qApp->desktop()->width()-helpWindow->size().width()-horizSpace, vertSpace));

    // Default language to be used by OpenCOR

    setLocale(QLocale::system().name());

    // Default text size multiplier for the help widget

    helpWindow->setHelpWidgetTextSizeMultiplier(1.0);
}
<|endoftext|>


<commit_before>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include <cstdlib>
#include <mutex>

#include "caf/async/consumer.hpp"
#include "caf/async/producer.hpp"
#include "caf/config.hpp"
#include "caf/defaults.hpp"
#include "caf/error.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/make_counted.hpp"
#include "caf/raise_error.hpp"
#include "caf/ref_counted.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
#include "caf/unit.hpp"

namespace caf::async {

/// Policy type for having `consume` call `on_error` immediately after the
/// producer has aborted even if the buffer still contains events.
struct prioritize_errors_t {
  static constexpr bool calls_on_error = true;
};

/// @relates prioritize_errors_t
constexpr auto prioritize_errors = prioritize_errors_t{};

/// Policy type for having `consume` call `on_error` only after processing all
/// events from the buffer.
struct delay_errors_t {
  static constexpr bool calls_on_error = true;
};

/// @relates delay_errors_t
constexpr auto delay_errors = delay_errors_t{};

/// A bounded buffer for transmitting events from one producer to one consumer.
template <class T>
class bounded_buffer : public ref_counted {
public:
  using value_type = T;

  bounded_buffer(uint32_t max_in_flight, uint32_t min_pull_size)
    : max_in_flight_(max_in_flight), min_pull_size_(min_pull_size) {
    buf_ = reinterpret_cast<T*>(malloc(sizeof(T) * max_in_flight * 2));
  }

  ~bounded_buffer() {
    auto first = buf_ + rd_pos_;
    auto last = buf_ + wr_pos_;
    std::destroy(first, last);
    free(buf_);
  }

  /// Appends to the buffer and calls `on_producer_wakeup` on the consumer if
  /// the buffer becomes non-empty.
  /// @returns the remaining capacity after inserting the items.
  size_t push(span<const T> items) {
    std::unique_lock guard{mtx_};
    CAF_ASSERT(producer_ != nullptr);
    CAF_ASSERT(!closed_);
    CAF_ASSERT(wr_pos_ + items.size() < max_in_flight_ * 2);
    std::uninitialized_copy(items.begin(), items.end(), buf_ + wr_pos_);
    wr_pos_ += items.size();
    if (size() == items.size() && consumer_)
      consumer_->on_producer_wakeup();
    return capacity() - size();
  }

  size_t push(const T& item) {
    return push(make_span(&item, 1));
  }

  /// Consumes up to `demand` items from the buffer.
  /// @tparam Policy Either `instant_error_t`, `delay_error_t` or
  ///                `ignore_errors_t`. The former two policies require also
  ///                passing an `on_error` handler.
  /// @returns true if the consumer may call `pull` again, otherwise `false`.
  ///          When returning `false`, the function has called `on_complete` or
  ///          `on_error` on the observer.
  template <class Policy, class Observer>
  std::pair<bool, size_t> pull(Policy, size_t demand, Observer& dst) {
    static constexpr size_t local_buf_size = 16;
    T local_buf[local_buf_size];
    std::unique_lock guard{mtx_};
    CAF_ASSERT(consumer_ != nullptr);
    if constexpr (std::is_same_v<Policy, prioritize_errors_t>) {
      if (err_) {
        consumer_ = nullptr;
        dst.on_error(err_);
        return {false, 0};
      }
    }
    auto next_n = [this, &demand] {
      return std::min({local_buf_size, demand, size()});
    };
    size_t consumed = 0;
    for (auto n = next_n(); n > 0; n = next_n()) {
      auto first = buf_ + rd_pos_;
      std::move(first, first + n, local_buf);
      std::destroy(first, first + n);
      rd_pos_ += n;
      shift_elements();
      signal_demand(n);
      guard.unlock();
      dst.on_next(make_span(local_buf, n));
      demand -= n;
      consumed += n;
      guard.lock();
    }
    if (!empty() || !closed_) {
      return {true, consumed};
    } else {
      consumer_ = nullptr;
      if (err_)
        dst.on_error(err_);
      else
        dst.on_complete();
      return {false, consumed};
    }
  }

  /// Checks whether there is any pending data in the buffer.
  bool has_data() const noexcept {
    std::unique_lock guard{mtx_};
    return !empty();
  }

  /// Checks whether the there is data available or the producer has closed or
  /// aborted the flow.
  bool has_consumer_event() const noexcept {
    std::unique_lock guard{mtx_};
    return !empty() || closed_;
  }

  /// Returns how many items are currently available.
  size_t available() const noexcept {
    std::unique_lock guard{mtx_};
    return size();
  }

  /// Closes the buffer by request of the producer.
  void close() {
    std::unique_lock guard{mtx_};
    if (producer_) {
      closed_ = true;
      producer_ = nullptr;
      if (empty() && consumer_)
        consumer_->on_producer_wakeup();
    }
  }

  /// Closes the buffer and signals an error by request of the producer.
  void abort(error reason) {
    std::unique_lock guard{mtx_};
    if (producer_) {
      closed_ = true;
      err_ = std::move(reason);
      producer_ = nullptr;
      if (empty() && consumer_)
        consumer_->on_producer_wakeup();
    }
  }

  /// Closes the buffer by request of the consumer.
  void cancel() {
    std::unique_lock guard{mtx_};
    if (consumer_) {
      consumer_ = nullptr;
      if (producer_)
        producer_->on_consumer_cancel();
    }
  }

  void set_consumer(consumer_ptr consumer) {
    CAF_ASSERT(consumer != nullptr);
    std::unique_lock guard{mtx_};
    if (consumer_)
      CAF_RAISE_ERROR("producer-consumer queue already has a consumer");
    consumer_ = std::move(consumer);
    if (producer_)
      ready();
  }

  void set_producer(producer_ptr producer) {
    CAF_ASSERT(producer != nullptr);
    std::unique_lock guard{mtx_};
    if (producer_)
      CAF_RAISE_ERROR("producer-consumer queue already has a producer");
    producer_ = std::move(producer);
    if (consumer_)
      ready();
  }

  size_t capacity() const noexcept {
    return max_in_flight_;
  }

private:
  void ready() {
    producer_->on_consumer_ready();
    consumer_->on_producer_ready();
    if (!empty())
      consumer_->on_producer_wakeup();
    signal_demand(max_in_flight_);
  }

  size_t empty() const noexcept {
    CAF_ASSERT(wr_pos_ >= rd_pos_);
    return wr_pos_ == rd_pos_;
  }

  size_t size() const noexcept {
    CAF_ASSERT(wr_pos_ >= rd_pos_);
    return wr_pos_ - rd_pos_;
  }

  void shift_elements() {
    if (rd_pos_ >= max_in_flight_) {
      if (empty()) {
        rd_pos_ = 0;
        wr_pos_ = 0;
      } else {
        // No need to check for overlap: the first half of the buffer is
        // empty.
        auto first = buf_ + rd_pos_;
        auto last = buf_ + wr_pos_;
        std::uninitialized_move(first, last, buf_);
        std::destroy(first, last);
        wr_pos_ -= rd_pos_;
        rd_pos_ = 0;
      }
    }
  }

  void signal_demand(uint32_t new_demand) {
    demand_ += new_demand;
    if (demand_ >= min_pull_size_ && producer_) {
      producer_->on_consumer_demand(demand_);
      demand_ = 0;
    }
  }

  /// Guards access to all other member variables.
  mutable std::mutex mtx_;

  /// Allocated to max_in_flight_ * 2, but at most holds max_in_flight_
  /// elements at any point in time. We dynamically shift elements into the
  /// first half of the buffer whenever rd_pos_ crosses the midpoint.
  T* buf_;

  /// Stores how many items the buffer may hold at any time.
  uint32_t max_in_flight_;

  /// Configures the minimum amount of free buffer slots that we signal to the
  /// producer.
  uint32_t min_pull_size_;

  /// Stores the read position of the consumer.
  uint32_t rd_pos_ = 0;

  /// Stores the write position of the producer.
  uint32_t wr_pos_ = 0;

  /// Demand that has not yet been signaled back to the producer.
  uint32_t demand_ = 0;

  /// Stores whether `close` has been called.
  bool closed_ = false;

  /// Stores the abort reason.
  error err_;

  /// Callback handle to the consumer.
  consumer_ptr consumer_;

  /// Callback handle to the producer.
  producer_ptr producer_;
};

/// @relates bounded_buffer
template <class T>
using bounded_buffer_ptr = intrusive_ptr<bounded_buffer<T>>;

/// @relates bounded_buffer
template <class T, bool IsProducer>
struct resource_ctrl : ref_counted {
  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit resource_ctrl(buffer_ptr ptr) : buf(std::move(ptr)) {
    // nop
  }

  ~resource_ctrl() {
    if (buf) {
      if constexpr (IsProducer) {
        auto err = make_error(sec::invalid_upstream,
                              "producer_resource destroyed without opening it");
        buf->abort(err);
      } else {
        buf->cancel();
      }
    }
  }

  buffer_ptr try_open() {
    std::unique_lock guard{mtx};
    if (buf) {
      auto res = buffer_ptr{};
      res.swap(buf);
      return res;
    }
    return nullptr;
  }

  mutable std::mutex mtx;
  buffer_ptr buf;
};

/// Grants read access to the first consumer that calls `open` on the resource.
/// Cancels consumption of items on the buffer if the resources gets destroyed
/// before opening it.
/// @relates bounded_buffer
template <class T>
class consumer_resource {
public:
  using value_type = T;

  using buffer_type = bounded_buffer<T>;

  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit consumer_resource(buffer_ptr buf) {
    ctrl_.emplace(std::move(buf));
  }

  consumer_resource() = default;
  consumer_resource(consumer_resource&&) = default;
  consumer_resource(const consumer_resource&) = default;
  consumer_resource& operator=(consumer_resource&&) = default;
  consumer_resource& operator=(const consumer_resource&) = default;

  /// Tries to open the resource for reading from the buffer. The first `open`
  /// wins on concurrent access.
  /// @returns a pointer to the buffer on success, `nullptr` otherwise.
  buffer_ptr try_open() {
    if (ctrl_) {
      auto res = ctrl_->try_open();
      ctrl_ = nullptr;
      return res;
    } else {
      return nullptr;
    }
  }

private:
  intrusive_ptr<resource_ctrl<T, false>> ctrl_;
};

/// Grants access to a buffer to the first producer that calls `open`. Aborts
/// writes on the buffer if the resources gets destroyed before opening it.
/// @relates bounded_buffer
template <class T>
class producer_resource {
public:
  using value_type = T;

  using buffer_type = bounded_buffer<T>;

  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit producer_resource(buffer_ptr buf) {
    ctrl_.emplace(std::move(buf));
  }

  producer_resource() = default;
  producer_resource(producer_resource&&) = default;
  producer_resource(const producer_resource&) = default;
  producer_resource& operator=(producer_resource&&) = default;
  producer_resource& operator=(const producer_resource&) = default;

  /// Tries to open the resource for writing to the buffer. The first `open`
  /// wins on concurrent access.
  /// @returns a pointer to the buffer on success, `nullptr` otherwise.
  buffer_ptr try_open() {
    if (ctrl_) {
      auto res = ctrl_->try_open();
      ctrl_ = nullptr;
      return res;
    } else {
      return nullptr;
    }
  }

private:
  intrusive_ptr<resource_ctrl<T, true>> ctrl_;
};

/// Creates bounded buffer and returns two resources connected by that buffer.
template <class T>
std::pair<consumer_resource<T>, producer_resource<T>>
make_bounded_buffer_resource(size_t buffer_size, size_t min_request_size) {
  using buffer_type = bounded_buffer<T>;
  auto buf = make_counted<buffer_type>(buffer_size, min_request_size);
  return {async::consumer_resource<T>{buf}, async::producer_resource<T>{buf}};
}

/// Creates bounded buffer and returns two resources connected by that buffer.
template <class T>
std::pair<consumer_resource<T>, producer_resource<T>>
make_bounded_buffer_resource() {
  return make_bounded_buffer_resource<T>(defaults::flow::buffer_size,
                                         defaults::flow::min_demand);
}

} // namespace caf::async
<commit_msg>Fix crash on the bounded buffer<commit_after>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include <cstdlib>
#include <mutex>

#include "caf/async/consumer.hpp"
#include "caf/async/producer.hpp"
#include "caf/config.hpp"
#include "caf/defaults.hpp"
#include "caf/error.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/make_counted.hpp"
#include "caf/raise_error.hpp"
#include "caf/ref_counted.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
#include "caf/unit.hpp"

namespace caf::async {

/// Policy type for having `consume` call `on_error` immediately after the
/// producer has aborted even if the buffer still contains events.
struct prioritize_errors_t {
  static constexpr bool calls_on_error = true;
};

/// @relates prioritize_errors_t
constexpr auto prioritize_errors = prioritize_errors_t{};

/// Policy type for having `consume` call `on_error` only after processing all
/// events from the buffer.
struct delay_errors_t {
  static constexpr bool calls_on_error = true;
};

/// @relates delay_errors_t
constexpr auto delay_errors = delay_errors_t{};

/// A bounded buffer for transmitting events from one producer to one consumer.
template <class T>
class bounded_buffer : public ref_counted {
public:
  using value_type = T;

  bounded_buffer(uint32_t max_in_flight, uint32_t min_pull_size)
    : max_in_flight_(max_in_flight), min_pull_size_(min_pull_size) {
    buf_.reserve(max_in_flight);
    consumer_buf_.reserve(max_in_flight);
  }

  /// Appends to the buffer and calls `on_producer_wakeup` on the consumer if
  /// the buffer becomes non-empty.
  /// @returns the remaining capacity after inserting the items.
  size_t push(span<const T> items) {
    std::unique_lock guard{mtx_};
    CAF_ASSERT(producer_ != nullptr);
    CAF_ASSERT(!closed_);
    CAF_ASSERT(buf_.size() + items.size() <= max_in_flight_);
    buf_.insert(buf_.end(), items.begin(), items.end());
    if (buf_.size() == items.size() && consumer_)
      consumer_->on_producer_wakeup();
    return capacity() - buf_.size();
  }

  size_t push(const T& item) {
    return push(make_span(&item, 1));
  }

  /// Consumes up to `demand` items from the buffer.
  /// @tparam Policy Either `instant_error_t`, `delay_error_t` or
  ///                `ignore_errors_t`. The former two policies require also
  ///                passing an `on_error` handler.
  /// @returns true if the consumer may call `pull` again, otherwise `false`.
  ///          When returning `false`, the function has called `on_complete` or
  ///          `on_error` on the observer.
  template <class Policy, class Observer>
  std::pair<bool, size_t> pull(Policy, size_t demand, Observer& dst) {
    std::unique_lock guard{mtx_};
    CAF_ASSERT(consumer_ != nullptr);
    CAF_ASSERT(consumer_buf_.empty());
    if constexpr (std::is_same_v<Policy, prioritize_errors_t>) {
      if (err_) {
        consumer_ = nullptr;
        dst.on_error(err_);
        return {false, 0};
      }
    }
    auto next_n = [this, &demand] { return std::min(demand, buf_.size()); };
    size_t consumed = 0;
    for (auto n = next_n(); n > 0; n = next_n()) {
      using std::make_move_iterator;
      consumer_buf_.assign(make_move_iterator(buf_.begin()),
                           make_move_iterator(buf_.begin() + n));
      buf_.erase(buf_.begin(), buf_.begin() + n);
      signal_demand(n);
      guard.unlock();
      dst.on_next(span<const T>{consumer_buf_.data(), n});
      demand -= n;
      consumed += n;
      consumer_buf_.clear();
      guard.lock();
    }
    if (!buf_.empty() || !closed_) {
      return {true, consumed};
    } else {
      consumer_ = nullptr;
      if (err_)
        dst.on_error(err_);
      else
        dst.on_complete();
      return {false, consumed};
    }
  }

  /// Checks whether there is any pending data in the buffer.
  bool has_data() const noexcept {
    std::unique_lock guard{mtx_};
    return !buf_.empty();
  }

  /// Checks whether the there is data available or the producer has closed or
  /// aborted the flow.
  bool has_consumer_event() const noexcept {
    std::unique_lock guard{mtx_};
    return !buf_.empty() || closed_;
  }

  /// Returns how many items are currently available.
  size_t available() const noexcept {
    std::unique_lock guard{mtx_};
    return buf_.size();
  }

  /// Closes the buffer by request of the producer.
  void close() {
    std::unique_lock guard{mtx_};
    if (producer_) {
      closed_ = true;
      producer_ = nullptr;
      if (buf_.empty() && consumer_)
        consumer_->on_producer_wakeup();
    }
  }

  /// Closes the buffer and signals an error by request of the producer.
  void abort(error reason) {
    std::unique_lock guard{mtx_};
    if (producer_) {
      closed_ = true;
      err_ = std::move(reason);
      producer_ = nullptr;
      if (buf_.empty() && consumer_)
        consumer_->on_producer_wakeup();
    }
  }

  /// Closes the buffer by request of the consumer.
  void cancel() {
    std::unique_lock guard{mtx_};
    if (consumer_) {
      consumer_ = nullptr;
      if (producer_)
        producer_->on_consumer_cancel();
    }
  }

  void set_consumer(consumer_ptr consumer) {
    CAF_ASSERT(consumer != nullptr);
    std::unique_lock guard{mtx_};
    if (consumer_)
      CAF_RAISE_ERROR("producer-consumer queue already has a consumer");
    consumer_ = std::move(consumer);
    if (producer_)
      ready();
  }

  void set_producer(producer_ptr producer) {
    CAF_ASSERT(producer != nullptr);
    std::unique_lock guard{mtx_};
    if (producer_)
      CAF_RAISE_ERROR("producer-consumer queue already has a producer");
    producer_ = std::move(producer);
    if (consumer_)
      ready();
  }

  size_t capacity() const noexcept {
    return max_in_flight_;
  }

private:
  void ready() {
    producer_->on_consumer_ready();
    consumer_->on_producer_ready();
    if (!buf_.empty())
      consumer_->on_producer_wakeup();
    signal_demand(max_in_flight_);
  }

  void signal_demand(uint32_t new_demand) {
    demand_ += new_demand;
    if (demand_ >= min_pull_size_ && producer_) {
      producer_->on_consumer_demand(demand_);
      demand_ = 0;
    }
  }

  /// Guards access to all other member variables.
  mutable std::mutex mtx_;

  /// Allocated to max_in_flight_ * 2, but at most holds max_in_flight_
  /// elements at any point in time. We dynamically shift elements into the
  /// first half of the buffer whenever rd_pos_ crosses the midpoint.
  std::vector<T> buf_;

  /// Stores how many items the buffer may hold at any time.
  uint32_t max_in_flight_;

  /// Configures the minimum amount of free buffer slots that we signal to the
  /// producer.
  uint32_t min_pull_size_;

  /// Demand that has not yet been signaled back to the producer.
  uint32_t demand_ = 0;

  /// Stores whether `close` has been called.
  bool closed_ = false;

  /// Stores the abort reason.
  error err_;

  /// Callback handle to the consumer.
  consumer_ptr consumer_;

  /// Callback handle to the producer.
  producer_ptr producer_;

  /// Caches elements before passing them to the consumer (without lock).
  std::vector<T> consumer_buf_;
};

/// @relates bounded_buffer
template <class T>
using bounded_buffer_ptr = intrusive_ptr<bounded_buffer<T>>;

/// @relates bounded_buffer
template <class T, bool IsProducer>
struct resource_ctrl : ref_counted {
  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit resource_ctrl(buffer_ptr ptr) : buf(std::move(ptr)) {
    // nop
  }

  ~resource_ctrl() {
    if (buf) {
      if constexpr (IsProducer) {
        auto err = make_error(sec::invalid_upstream,
                              "producer_resource destroyed without opening it");
        buf->abort(err);
      } else {
        buf->cancel();
      }
    }
  }

  buffer_ptr try_open() {
    std::unique_lock guard{mtx};
    if (buf) {
      auto res = buffer_ptr{};
      res.swap(buf);
      return res;
    }
    return nullptr;
  }

  mutable std::mutex mtx;
  buffer_ptr buf;
};

/// Grants read access to the first consumer that calls `open` on the resource.
/// Cancels consumption of items on the buffer if the resources gets destroyed
/// before opening it.
/// @relates bounded_buffer
template <class T>
class consumer_resource {
public:
  using value_type = T;

  using buffer_type = bounded_buffer<T>;

  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit consumer_resource(buffer_ptr buf) {
    ctrl_.emplace(std::move(buf));
  }

  consumer_resource() = default;
  consumer_resource(consumer_resource&&) = default;
  consumer_resource(const consumer_resource&) = default;
  consumer_resource& operator=(consumer_resource&&) = default;
  consumer_resource& operator=(const consumer_resource&) = default;

  /// Tries to open the resource for reading from the buffer. The first `open`
  /// wins on concurrent access.
  /// @returns a pointer to the buffer on success, `nullptr` otherwise.
  buffer_ptr try_open() {
    if (ctrl_) {
      auto res = ctrl_->try_open();
      ctrl_ = nullptr;
      return res;
    } else {
      return nullptr;
    }
  }

private:
  intrusive_ptr<resource_ctrl<T, false>> ctrl_;
};

/// Grants access to a buffer to the first producer that calls `open`. Aborts
/// writes on the buffer if the resources gets destroyed before opening it.
/// @relates bounded_buffer
template <class T>
class producer_resource {
public:
  using value_type = T;

  using buffer_type = bounded_buffer<T>;

  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit producer_resource(buffer_ptr buf) {
    ctrl_.emplace(std::move(buf));
  }

  producer_resource() = default;
  producer_resource(producer_resource&&) = default;
  producer_resource(const producer_resource&) = default;
  producer_resource& operator=(producer_resource&&) = default;
  producer_resource& operator=(const producer_resource&) = default;

  /// Tries to open the resource for writing to the buffer. The first `open`
  /// wins on concurrent access.
  /// @returns a pointer to the buffer on success, `nullptr` otherwise.
  buffer_ptr try_open() {
    if (ctrl_) {
      auto res = ctrl_->try_open();
      ctrl_ = nullptr;
      return res;
    } else {
      return nullptr;
    }
  }

private:
  intrusive_ptr<resource_ctrl<T, true>> ctrl_;
};

/// Creates bounded buffer and returns two resources connected by that buffer.
template <class T>
std::pair<consumer_resource<T>, producer_resource<T>>
make_bounded_buffer_resource(size_t buffer_size, size_t min_request_size) {
  using buffer_type = bounded_buffer<T>;
  auto buf = make_counted<buffer_type>(buffer_size, min_request_size);
  return {async::consumer_resource<T>{buf}, async::producer_resource<T>{buf}};
}

/// Creates bounded buffer and returns two resources connected by that buffer.
template <class T>
std::pair<consumer_resource<T>, producer_resource<T>>
make_bounded_buffer_resource() {
  return make_bounded_buffer_resource<T>(defaults::flow::buffer_size,
                                         defaults::flow::min_demand);
}

} // namespace caf::async
<|endoftext|>


<commit_before>// ======================================================================== //
// Copyright 2009-2015 Intel Corporation                                    //
//                                                                          //
// 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.                                           //
// ======================================================================== //

#include "subdivpatch1cached_intersector1.h"

namespace embree
{
  namespace isa
  {
    GridSOA::GridSOA(const SubdivPatch1Base& patch,
                     const size_t x0, const size_t x1, const size_t y0, const size_t y1, const size_t swidth, const size_t sheight,
                     const SubdivMesh* const geom, const size_t bvhBytes, BBox3fa* bounds_o)
      : root(BVH4::emptyNode), width(x1-x0+1), height(y1-y0+1), dim_offset(0), geomID(patch.geom), primID(patch.prim), bvhBytes(bvhBytes)
    {
      dim_offset = calculate_grid_size(width,height);

      dynamic_large_stack_array(float,local_grid_u,dim_offset+vfloat::size,64*64);
      dynamic_large_stack_array(float,local_grid_v,dim_offset+vfloat::size,64*64);
      dynamic_large_stack_array(float,local_grid_x,dim_offset+vfloat::size,64*64);
      dynamic_large_stack_array(float,local_grid_y,dim_offset+vfloat::size,64*64);
      dynamic_large_stack_array(float,local_grid_z,dim_offset+vfloat::size,64*64);

      /* compute vertex grid (+displacement) */
      evalGrid(patch,x0,x1,y0,y1,swidth,sheight,
               local_grid_x,local_grid_y,local_grid_z,local_grid_u,local_grid_v,geom);

      /* copy temporary data to tessellation cache */
      float* const grid_x  = (float*)(gridData() + 0*dim_offset);
      float* const grid_y  = (float*)(gridData() + 1*dim_offset);
      float* const grid_z  = (float*)(gridData() + 2*dim_offset);
      int  * const grid_uv = (int*  )(gridData() + 3*dim_offset);

      /* copy points */
      memcpy(grid_x, local_grid_x, dim_offset*sizeof(float));
      memcpy(grid_y, local_grid_y, dim_offset*sizeof(float));
      memcpy(grid_z, local_grid_z, dim_offset*sizeof(float));

      /* encode UVs */
      for (size_t i=0; i<dim_offset; i+=vfloat::size) {
        const vint iu = (vint) clamp(vfloat::load(&local_grid_u[i])*0xFFFF, vfloat(0.0f), vfloat(0xFFFF));
        const vint iv = (vint) clamp(vfloat::load(&local_grid_v[i])*0xFFFF, vfloat(0.0f), vfloat(0xFFFF));
        vint::storeu(&grid_uv[i], (iv << 16) | iu);
      }

      /* create BVH */
      root = buildBVH(bvhData(),gridData(),bvhBytes,bounds_o);
    }

    size_t GridSOA::getBVHBytes(const GridRange& range, const unsigned int leafBytes)
    {
      if (range.hasLeafSize())
        return leafBytes;

      __aligned(64) GridRange r[4];
      const unsigned int children = range.splitIntoSubRanges(r);

      size_t bytes = sizeof(BVH4::Node);
      for (unsigned int i=0;i<children;i++)
        bytes += getBVHBytes(r[i],leafBytes);
      return bytes;
    }

    BVH4::NodeRef GridSOA::buildBVH(char* node_array, float* grid_array, const size_t bvhBytes, BBox3fa* bounds_o)
    {
      BVH4::NodeRef root = 0; size_t allocator = 0;
      GridRange range(0,width-1,0,height-1);
      BBox3fa bounds = buildBVH(root,node_array,grid_array,range,allocator);
      if (bounds_o) *bounds_o = bounds;
      assert(allocator == bvhBytes);
      return root;
    }

    BBox3fa GridSOA::buildBVH(BVH4::NodeRef& curNode, char* node_array, float* grid_array, const GridRange& range, size_t& allocator)
    {
      /*! create leaf node */
      if (unlikely(range.hasLeafSize()))
      {
        const float* const grid_x_array = grid_array + 0 * dim_offset;
        const float* const grid_y_array = grid_array + 1 * dim_offset;
        const float* const grid_z_array = grid_array + 2 * dim_offset;

        /* compute the bounds just for the range! */
        BBox3fa bounds( empty );
        for (size_t v = range.v_start; v<=range.v_end; v++)
        {
          for (size_t u = range.u_start; u<=range.u_end; u++)
          {
            const float x = grid_x_array[ v * width + u];
            const float y = grid_y_array[ v * width + u];
            const float z = grid_z_array[ v * width + u];
            bounds.extend( Vec3fa(x,y,z) );
          }
        }
        assert(is_finite(bounds));

        /* shift 2x2 quads that wrap around to the left */ // FIXME: causes intersection filter to be called multiple times for some triangles
        unsigned int u_start = range.u_start, u_end = range.u_end;
        unsigned int v_start = range.v_start, v_end = range.v_end;
        unsigned int u_size = u_end-u_start; assert(u_size > 0);
        unsigned int v_size = v_end-v_start; assert(v_size > 0);
        if (unlikely(u_size < 2 && u_start > 0)) u_start--;
        if (unlikely(v_size < 2 && v_start > 0)) v_start--;

        /* we store pointer to first subgrid vertex as leaf node */
        const size_t value = 16*(v_start * width + u_start + 1); // +1 to not create empty leaf
        curNode = BVH4::encodeTypedLeaf((void*)value,0);
        return bounds;
      }

      /* create internal node */
      else
      {
        /* allocate new bvh4 node */
        BVH4::Node* node = (BVH4::Node *)&node_array[allocator];
        allocator += sizeof(BVH4::Node);
        node->clear();

        /* split range */
        GridRange r[4];
        const size_t children = range.splitIntoSubRanges(r);

        /* recurse into subtrees */
        BBox3fa bounds( empty );
        for (size_t i=0; i<children; i++)
        {
          BBox3fa box = buildBVH( node->child(i), node_array, grid_array, r[i], allocator);
          node->set(i,box);
          bounds.extend(box);
        }

        curNode = BVH4::encodeNode(node);
        assert(is_finite(bounds));
        return bounds;
      }
    }
  }
}
<commit_msg>updates<commit_after>// ======================================================================== //
// Copyright 2009-2015 Intel Corporation                                    //
//                                                                          //
// 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.                                           //
// ======================================================================== //

#include "grid_soa.h"

namespace embree
{
  namespace isa
  {
    GridSOA::GridSOA(const SubdivPatch1Base& patch,
                     const size_t x0, const size_t x1, const size_t y0, const size_t y1, const size_t swidth, const size_t sheight,
                     const SubdivMesh* const geom, const size_t bvhBytes, BBox3fa* bounds_o)
      : root(BVH4::emptyNode), width(x1-x0+1), height(y1-y0+1), dim_offset(0), geomID(patch.geom), primID(patch.prim), bvhBytes(bvhBytes)
    {
      dim_offset = calculate_grid_size(width,height);

      dynamic_large_stack_array(float,local_grid_u,dim_offset+vfloat::size,64*64);
      dynamic_large_stack_array(float,local_grid_v,dim_offset+vfloat::size,64*64);
      dynamic_large_stack_array(float,local_grid_x,dim_offset+vfloat::size,64*64);
      dynamic_large_stack_array(float,local_grid_y,dim_offset+vfloat::size,64*64);
      dynamic_large_stack_array(float,local_grid_z,dim_offset+vfloat::size,64*64);

      /* compute vertex grid (+displacement) */
      evalGrid(patch,x0,x1,y0,y1,swidth,sheight,
               local_grid_x,local_grid_y,local_grid_z,local_grid_u,local_grid_v,geom);

      /* copy temporary data to tessellation cache */
      float* const grid_x  = (float*)(gridData() + 0*dim_offset);
      float* const grid_y  = (float*)(gridData() + 1*dim_offset);
      float* const grid_z  = (float*)(gridData() + 2*dim_offset);
      int  * const grid_uv = (int*  )(gridData() + 3*dim_offset);

      /* copy points */
      memcpy(grid_x, local_grid_x, dim_offset*sizeof(float));
      memcpy(grid_y, local_grid_y, dim_offset*sizeof(float));
      memcpy(grid_z, local_grid_z, dim_offset*sizeof(float));

      /* encode UVs */
      for (size_t i=0; i<dim_offset; i+=vfloat::size) {
        const vint iu = (vint) clamp(vfloat::load(&local_grid_u[i])*0xFFFF, vfloat(0.0f), vfloat(0xFFFF));
        const vint iv = (vint) clamp(vfloat::load(&local_grid_v[i])*0xFFFF, vfloat(0.0f), vfloat(0xFFFF));
        vint::storeu(&grid_uv[i], (iv << 16) | iu);
      }

      /* create BVH */
      root = buildBVH(bvhData(),gridData(),bvhBytes,bounds_o);
    }

    size_t GridSOA::getBVHBytes(const GridRange& range, const unsigned int leafBytes)
    {
      if (range.hasLeafSize())
        return leafBytes;

      __aligned(64) GridRange r[4];
      const unsigned int children = range.splitIntoSubRanges(r);

      size_t bytes = sizeof(BVH4::Node);
      for (unsigned int i=0;i<children;i++)
        bytes += getBVHBytes(r[i],leafBytes);
      return bytes;
    }

    BVH4::NodeRef GridSOA::buildBVH(char* node_array, float* grid_array, const size_t bvhBytes, BBox3fa* bounds_o)
    {
      BVH4::NodeRef root = 0; size_t allocator = 0;
      GridRange range(0,width-1,0,height-1);
      BBox3fa bounds = buildBVH(root,node_array,grid_array,range,allocator);
      if (bounds_o) *bounds_o = bounds;
      assert(allocator == bvhBytes);
      return root;
    }

    BBox3fa GridSOA::buildBVH(BVH4::NodeRef& curNode, char* node_array, float* grid_array, const GridRange& range, size_t& allocator)
    {
      /*! create leaf node */
      if (unlikely(range.hasLeafSize()))
      {
        const float* const grid_x_array = grid_array + 0 * dim_offset;
        const float* const grid_y_array = grid_array + 1 * dim_offset;
        const float* const grid_z_array = grid_array + 2 * dim_offset;

        /* compute the bounds just for the range! */
        BBox3fa bounds( empty );
        for (size_t v = range.v_start; v<=range.v_end; v++)
        {
          for (size_t u = range.u_start; u<=range.u_end; u++)
          {
            const float x = grid_x_array[ v * width + u];
            const float y = grid_y_array[ v * width + u];
            const float z = grid_z_array[ v * width + u];
            bounds.extend( Vec3fa(x,y,z) );
          }
        }
        assert(is_finite(bounds));

        /* shift 2x2 quads that wrap around to the left */ // FIXME: causes intersection filter to be called multiple times for some triangles
        unsigned int u_start = range.u_start, u_end = range.u_end;
        unsigned int v_start = range.v_start, v_end = range.v_end;
        unsigned int u_size = u_end-u_start; assert(u_size > 0);
        unsigned int v_size = v_end-v_start; assert(v_size > 0);
        if (unlikely(u_size < 2 && u_start > 0)) u_start--;
        if (unlikely(v_size < 2 && v_start > 0)) v_start--;

        /* we store pointer to first subgrid vertex as leaf node */
        const size_t value = 16*(v_start * width + u_start + 1); // +1 to not create empty leaf
        curNode = BVH4::encodeTypedLeaf((void*)value,0);
        return bounds;
      }

      /* create internal node */
      else
      {
        /* allocate new bvh4 node */
        BVH4::Node* node = (BVH4::Node *)&node_array[allocator];
        allocator += sizeof(BVH4::Node);
        node->clear();

        /* split range */
        GridRange r[4];
        const size_t children = range.splitIntoSubRanges(r);

        /* recurse into subtrees */
        BBox3fa bounds( empty );
        for (size_t i=0; i<children; i++)
        {
          BBox3fa box = buildBVH( node->child(i), node_array, grid_array, r[i], allocator);
          node->set(i,box);
          bounds.extend(box);
        }

        curNode = BVH4::encodeNode(node);
        assert(is_finite(bounds));
        return bounds;
      }
    }
  }
}
<|endoftext|>


<commit_before>/*
** Author(s):
**  - Cedric GESTES <gestes@aldebaran-robotics.com>
**
** Copyright (C) 2010, 2012 Aldebaran Robotics
*/

#include <iostream>
#include <vector>
#include <map>

#include <qimessaging/object.hpp>
#include <qimessaging/transport_server.hpp>
#include <qimessaging/transport_socket.hpp>
#include <qimessaging/service_directory.hpp>
#include <qimessaging/session.hpp>
#include <qimessaging/datastream.hpp>
#include <qimessaging/service_info.hpp>
#include "src/network_thread.hpp"
#include "src/transport_server_p.hpp"
#include "src/server_functor_result_future_p.hpp"
#include <qi/os.hpp>
#include <qi/log.hpp>

namespace qi
{

  class ServiceDirectoryPrivate : public TransportServerInterface,
      public TransportSocketInterface,
      public Object
  {
  public:
    ServiceDirectoryPrivate();
    ~ServiceDirectoryPrivate();

    virtual void newConnection();
    virtual void onSocketReadyRead(TransportSocket *socket, int id);
    virtual void onSocketWriteDone(TransportSocket *client);
    virtual void onSocketConnected(TransportSocket *client);
    virtual void onSocketDisconnected(TransportSocket *client);

    std::vector<ServiceInfo> services();
    ServiceInfo              service(const std::string &name);
    unsigned int             registerService(const ServiceInfo &svcinfo);
    void                     unregisterService(const unsigned int &idx);

  public:
    qi::NetworkThread                                 *nthd;
    qi::TransportServer                               *ts;
    std::map<unsigned int, ServiceInfo>                connectedServices;
    std::map<std::string, unsigned int>                nameToIdx;
    unsigned int                                       servicesCount;
  }; // !ServiceDirectoryPrivate

  ServiceDirectoryPrivate::ServiceDirectoryPrivate()
    : nthd(new qi::NetworkThread())
    , ts(new qi::TransportServer())
    , servicesCount(0)
  {
    ts->setCallbacks(this);

    ServiceInfo si;
    si.setName("serviceDirectory");
    si.setServiceId(1);
    unsigned int regid = registerService(si);
    //serviceDirectory must have id '1'
    assert(regid == 1);

    /*
     * Order is important. See qi::Message::ServiceDirectoryFunctions.
     */
    advertiseMethod("service", this, &ServiceDirectoryPrivate::service);
    advertiseMethod("services", this, &ServiceDirectoryPrivate::services);
    advertiseMethod("registerService", this, &ServiceDirectoryPrivate::registerService);
    advertiseMethod("unregisterService", this, &ServiceDirectoryPrivate::unregisterService);
  }

  ServiceDirectoryPrivate::~ServiceDirectoryPrivate()
  {
    delete ts;
    delete nthd;
  }

  void ServiceDirectoryPrivate::newConnection()
  {
    TransportSocket *socket = ts->nextPendingConnection();
    if (!socket)
      return;
    socket->setCallbacks(this);
  }

  void ServiceDirectoryPrivate::onSocketReadyRead(TransportSocket *socket, int id)
  {
    qi::Message msg;
    socket->read(id, &msg);
    FunctorParameters din(msg.buffer());

    ServerFunctorResult fr(socket, msg);
    metaCall(msg.function(), "sig", din, fr);
  }

  void ServiceDirectoryPrivate::onSocketWriteDone(TransportSocket *client)
  {
  }

  void ServiceDirectoryPrivate::onSocketConnected(TransportSocket *client)
  {
  }

  void ServiceDirectoryPrivate::onSocketDisconnected(TransportSocket *client)
  {
  }

  std::vector<ServiceInfo> ServiceDirectoryPrivate::services()
  {
    std::vector<ServiceInfo> result;
    std::map<unsigned int, ServiceInfo>::const_iterator it;

    for (it = connectedServices.begin(); it != connectedServices.end(); ++it)
      result.push_back(it->second);

    return result;
  }

  ServiceInfo ServiceDirectoryPrivate::service(const std::string &name)
  {
    std::map<unsigned int, ServiceInfo>::const_iterator servicesIt;
    std::map<std::string, unsigned int>::const_iterator it;

    it = nameToIdx.find(name);
    if (it == nameToIdx.end())
      return ServiceInfo();

    unsigned int idx = it->second;

    servicesIt = connectedServices.find(idx);
    if (servicesIt == connectedServices.end())
      return ServiceInfo();

    return servicesIt->second;
  }

  unsigned int ServiceDirectoryPrivate::registerService(const ServiceInfo &svcinfo)
  {
    unsigned int idx = ++servicesCount;
    nameToIdx[svcinfo.name()] = idx;
    connectedServices[idx] = svcinfo;
    connectedServices[idx].setServiceId(idx);
    qiLogInfo("qimessaging.ServiceDirectory")  << "service " << svcinfo.name() << " registered #" << idx << std::endl;
    return idx;
  }

  void ServiceDirectoryPrivate::unregisterService(const unsigned int &idx)
  {
    std::map<std::string, unsigned int>::iterator it;
    it = nameToIdx.find(connectedServices[idx].name());
    if (it != nameToIdx.end())
    {
      qiLogInfo("qimessaging.ServiceDirectory") << "service "
                                                << connectedServices[idx].name()
                                                << " (#" << idx << ") unregistered"
                                                << std::endl;
      nameToIdx.erase(it);

      std::map<unsigned int, ServiceInfo>::iterator it2;
      it2 = connectedServices.find(idx);
      connectedServices.erase(it2);
    }
  }

ServiceDirectory::ServiceDirectory()
  : _p(new ServiceDirectoryPrivate())
{
}

ServiceDirectory::~ServiceDirectory()
{
  delete _p;
}

bool ServiceDirectory::listen(const std::string &address)
{
  qi::Url                  url(address);
  std::vector<std::string> eps;
  ServiceInfo             &si = _p->connectedServices[1];

  eps.push_back(address);
  si.setEndpoints(eps);

  return _p->ts->_p->start(_p->nthd->getEventBase(), url);
}

void ServiceDirectory::join()
{
  _p->nthd->join();
}

}; // !qi
<commit_msg>Handle service disconnection.<commit_after>/*
** Author(s):
**  - Cedric GESTES <gestes@aldebaran-robotics.com>
**
** Copyright (C) 2010, 2012 Aldebaran Robotics
*/

#include <iostream>
#include <vector>
#include <map>

#include <qimessaging/object.hpp>
#include <qimessaging/transport_server.hpp>
#include <qimessaging/transport_socket.hpp>
#include <qimessaging/service_directory.hpp>
#include <qimessaging/session.hpp>
#include <qimessaging/datastream.hpp>
#include <qimessaging/service_info.hpp>
#include "src/network_thread.hpp"
#include "src/transport_server_p.hpp"
#include "src/server_functor_result_future_p.hpp"
#include <qi/os.hpp>
#include <qi/log.hpp>

namespace qi
{

  class ServiceDirectoryPrivate : public TransportServerInterface,
      public TransportSocketInterface,
      public Object
  {
  public:
    ServiceDirectoryPrivate();
    ~ServiceDirectoryPrivate();

    virtual void newConnection();
    virtual void onSocketReadyRead(TransportSocket *socket, int id);
    virtual void onSocketWriteDone(TransportSocket *client);
    virtual void onSocketConnected(TransportSocket *client);
    virtual void onSocketDisconnected(TransportSocket *client);

    std::vector<ServiceInfo> services();
    ServiceInfo              service(const std::string &name);
    unsigned int             registerService(const ServiceInfo &svcinfo);
    void                     unregisterService(const unsigned int &idx);
    TransportSocket         *socket() { return currentSocket; }

  public:
    qi::NetworkThread                                     *nthd;
    qi::TransportServer                                   *ts;
    std::map<unsigned int, ServiceInfo>                    connectedServices;
    std::map<std::string, unsigned int>                    nameToIdx;
    std::map<TransportSocket*, std::vector<unsigned int> > socketToIdx;
    unsigned int                                           servicesCount;
    TransportSocket                                       *currentSocket;
  }; // !ServiceDirectoryPrivate

  ServiceDirectoryPrivate::ServiceDirectoryPrivate()
    : nthd(new qi::NetworkThread())
    , ts(new qi::TransportServer())
    , servicesCount(0)
    , currentSocket(0)
  {
    ts->setCallbacks(this);

    ServiceInfo si;
    si.setName("serviceDirectory");
    si.setServiceId(1);
    unsigned int regid = registerService(si);
    //serviceDirectory must have id '1'
    assert(regid == 1);

    /*
     * Order is important. See qi::Message::ServiceDirectoryFunctions.
     */
    advertiseMethod("service", this, &ServiceDirectoryPrivate::service);
    advertiseMethod("services", this, &ServiceDirectoryPrivate::services);
    advertiseMethod("registerService", this, &ServiceDirectoryPrivate::registerService);
    advertiseMethod("unregisterService", this, &ServiceDirectoryPrivate::unregisterService);
  }

  ServiceDirectoryPrivate::~ServiceDirectoryPrivate()
  {
    delete ts;
    delete nthd;
  }

  void ServiceDirectoryPrivate::newConnection()
  {
    TransportSocket *socket = ts->nextPendingConnection();
    if (!socket)
      return;
    socket->setCallbacks(this);
  }

  void ServiceDirectoryPrivate::onSocketReadyRead(TransportSocket *socket, int id)
  {
    currentSocket  = socket;

    qi::Message msg;
    socket->read(id, &msg);
    FunctorParameters din(msg.buffer());

    ServerFunctorResult fr(socket, msg);
    metaCall(msg.function(), "sig", din, fr);

    currentSocket  = 0;
  }

  void ServiceDirectoryPrivate::onSocketWriteDone(TransportSocket *socket)
  {
    currentSocket = socket;

    currentSocket = 0;
  }

  void ServiceDirectoryPrivate::onSocketConnected(TransportSocket *socket)
  {
    currentSocket = socket;

    currentSocket = 0;
  }

  void ServiceDirectoryPrivate::onSocketDisconnected(TransportSocket *socket)
  {
    currentSocket = socket;

    // if services were connected behind the socket
    std::map<TransportSocket*, std::vector<unsigned int> >::iterator it;
    if ((it = socketToIdx.find(socket)) == socketToIdx.end())
    {
      return;
    }

    for (std::vector<unsigned int>::iterator it2 = it->second.begin();
         it2 != it->second.end();
         it2++)
    {
      qiLogInfo("qimessaging.ServiceDirectory") << "service "
                                                << connectedServices[*it2].name()
                                                << " (#" << *it2 << ") disconnected"
                                                << std::endl;
      unregisterService(*it2);
    }

    currentSocket = 0;
  }

  std::vector<ServiceInfo> ServiceDirectoryPrivate::services()
  {
    std::vector<ServiceInfo> result;
    std::map<unsigned int, ServiceInfo>::const_iterator it;

    for (it = connectedServices.begin(); it != connectedServices.end(); ++it)
      result.push_back(it->second);

    return result;
  }

  ServiceInfo ServiceDirectoryPrivate::service(const std::string &name)
  {
    std::map<unsigned int, ServiceInfo>::const_iterator servicesIt;
    std::map<std::string, unsigned int>::const_iterator it;

    it = nameToIdx.find(name);
    if (it == nameToIdx.end())
      return ServiceInfo();

    unsigned int idx = it->second;

    servicesIt = connectedServices.find(idx);
    if (servicesIt == connectedServices.end())
      return ServiceInfo();

    return servicesIt->second;
  }

  unsigned int ServiceDirectoryPrivate::registerService(const ServiceInfo &svcinfo)
  {
    unsigned int idx = ++servicesCount;
    nameToIdx[svcinfo.name()] = idx;
    socketToIdx[socket()].push_back(idx);
    connectedServices[idx] = svcinfo;
    connectedServices[idx].setServiceId(idx);
    qiLogInfo("qimessaging.ServiceDirectory")  << "service " << svcinfo.name() << " registered #" << idx << std::endl;
    return idx;
  }

  void ServiceDirectoryPrivate::unregisterService(const unsigned int &idx)
  {
    std::map<std::string, unsigned int>::iterator it;
    it = nameToIdx.find(connectedServices[idx].name());
    if (it != nameToIdx.end())
    {
      qiLogInfo("qimessaging.ServiceDirectory") << "service "
                                                << connectedServices[idx].name()
                                                << " (#" << idx << ") unregistered"
                                                << std::endl;
      nameToIdx.erase(it);

      std::map<unsigned int, ServiceInfo>::iterator it2;
      it2 = connectedServices.find(idx);
      connectedServices.erase(it2);

      std::map<TransportSocket*, std::vector<unsigned int> >::iterator it3;
      it3 = socketToIdx.find(socket());
      socketToIdx.erase(it3);
    }
  }

ServiceDirectory::ServiceDirectory()
  : _p(new ServiceDirectoryPrivate())
{
}

ServiceDirectory::~ServiceDirectory()
{
  delete _p;
}

bool ServiceDirectory::listen(const std::string &address)
{
  qi::Url                  url(address);
  std::vector<std::string> eps;
  ServiceInfo             &si = _p->connectedServices[1];

  eps.push_back(address);
  si.setEndpoints(eps);

  return _p->ts->_p->start(_p->nthd->getEventBase(), url);
}

void ServiceDirectory::join()
{
  _p->nthd->join();
}

}; // !qi
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "dsb/execution/controller.hpp"

using namespace dsb::execution;

TEST(dsb_execution, Controller)
{
    const auto execLoc =
        dsb::execution::Locator("tcp://localhost:59999", "", "", "", "", "");
    auto exec = dsb::execution::Controller(execLoc);

    // Unfortunately, at the current time, we can only check whether the
    // functions correctly handle the most trivial errors, and not whether they
    // actually do what they're supposed to do.  The latter requires us to have
    // actual slaves present, which is hard to achieve in simple unittests.

    EXPECT_NO_THROW(exec.SetSimulationTime(1.0));
    EXPECT_NO_THROW(exec.SetSimulationTime(1.0, 2.0));
    EXPECT_THROW(exec.SetSimulationTime(2.0, 1.0), std::logic_error);
    EXPECT_NO_THROW(exec.SetSimulationTime(1.0, 2.0));

    EXPECT_NO_THROW(exec.AddSlave(1));
    EXPECT_NO_THROW(exec.AddSlave(2));
    EXPECT_THROW(exec.AddSlave(1), std::logic_error);
    EXPECT_NO_THROW(exec.AddSlave(3));
    EXPECT_THROW(exec.SetSimulationTime(1.0, 2.0), std::logic_error);

    EXPECT_NO_THROW(exec.SetVariables(1, dsb::sequence::EmptySequence<dsb::model::VariableValue>()));
    dsb::model::VariableValue v[2] = { { 1, 3.14 }, { 2, -10.0 } };
    EXPECT_NO_THROW(exec.SetVariables(2, dsb::sequence::ElementsOf(v, 2)));
    EXPECT_THROW(exec.SetVariables(4, dsb::sequence::EmptySequence<dsb::model::VariableValue>()), std::logic_error);
    EXPECT_THROW(exec.SetVariables(5, dsb::sequence::ElementsOf(v, 2)), std::logic_error);

    EXPECT_NO_THROW(exec.ConnectVariables(1, dsb::sequence::EmptySequence<dsb::model::VariableConnection>()));
    dsb::model::VariableConnection c[3] = { { 0, 2, 0 }, { 0, 3, 0 }, { 0, 4, 0 } };
    EXPECT_NO_THROW(exec.ConnectVariables(2, dsb::sequence::ElementsOf(c, 2)));
    EXPECT_THROW(exec.ConnectVariables(4, dsb::sequence::EmptySequence<dsb::model::VariableConnection>()), std::logic_error);
    EXPECT_THROW(exec.ConnectVariables(5, dsb::sequence::ElementsOf(c, 2)), std::logic_error);
    // The third connection has an invalid slave ID (4).
    EXPECT_THROW(exec.ConnectVariables(1, dsb::sequence::ElementsOf(c, 3)), std::logic_error);

    EXPECT_NO_THROW(exec.Terminate());
}
<commit_msg>Disabled test for dsb::execution::Controller<commit_after>// This test is disabled.  It doesn't work anymore, due to changes in the
// execution code that require there to be a broker present for the whole thing
// to work.  We need to figure this out.

/*

#include "gtest/gtest.h"
#include "dsb/execution/controller.hpp"

using namespace dsb::execution;

TEST(dsb_execution, Controller)
{
    const auto execLoc =
        dsb::execution::Locator("tcp://localhost:59999", "", "", "", "", "");
    auto exec = dsb::execution::Controller(execLoc);

    // Unfortunately, at the current time, we can only check whether the
    // functions correctly handle the most trivial errors, and not whether they
    // actually do what they're supposed to do.  The latter requires us to have
    // actual slaves present, which is hard to achieve in simple unittests.

    EXPECT_NO_THROW(exec.SetSimulationTime(1.0));
    EXPECT_NO_THROW(exec.SetSimulationTime(1.0, 2.0));
    EXPECT_THROW(exec.SetSimulationTime(2.0, 1.0), std::logic_error);
    EXPECT_NO_THROW(exec.SetSimulationTime(1.0, 2.0));

    EXPECT_NO_THROW(exec.AddSlave(1));
    EXPECT_NO_THROW(exec.AddSlave(2));
    EXPECT_THROW(exec.AddSlave(1), std::logic_error);
    EXPECT_NO_THROW(exec.AddSlave(3));
    EXPECT_THROW(exec.SetSimulationTime(1.0, 2.0), std::logic_error);

    EXPECT_NO_THROW(exec.SetVariables(1, dsb::sequence::EmptySequence<dsb::model::VariableValue>()));
    dsb::model::VariableValue v[2] = { { 1, 3.14 }, { 2, -10.0 } };
    EXPECT_NO_THROW(exec.SetVariables(2, dsb::sequence::ElementsOf(v, 2)));
    EXPECT_THROW(exec.SetVariables(4, dsb::sequence::EmptySequence<dsb::model::VariableValue>()), std::logic_error);
    EXPECT_THROW(exec.SetVariables(5, dsb::sequence::ElementsOf(v, 2)), std::logic_error);

    EXPECT_NO_THROW(exec.ConnectVariables(1, dsb::sequence::EmptySequence<dsb::model::VariableConnection>()));
    dsb::model::VariableConnection c[3] = { { 0, 2, 0 }, { 0, 3, 0 }, { 0, 4, 0 } };
    EXPECT_NO_THROW(exec.ConnectVariables(2, dsb::sequence::ElementsOf(c, 2)));
    EXPECT_THROW(exec.ConnectVariables(4, dsb::sequence::EmptySequence<dsb::model::VariableConnection>()), std::logic_error);
    EXPECT_THROW(exec.ConnectVariables(5, dsb::sequence::ElementsOf(c, 2)), std::logic_error);
    // The third connection has an invalid slave ID (4).
    EXPECT_THROW(exec.ConnectVariables(1, dsb::sequence::ElementsOf(c, 3)), std::logic_error);

    EXPECT_NO_THROW(exec.Terminate());
}

*/
<|endoftext|>


<commit_before>#include "Renderer.hpp"
#include "../OpenGL/OpenGLRenderer.hpp"
#include "../Vulkan/VulkanRenderer.hpp"

Renderer::~Renderer() {

}

Renderer* Renderer::makeRenderer(BACKEND backend){
    // Create and return backend.
    if(backend == BACKEND::OpenGL)
        return new VulkanRenderer();

    else if(backend == BACKEND::Vulkan)
        return new OpenGLRenderer();

    return nullptr;
}
<commit_msg>Added static window and undid woopsie.<commit_after>#include "Renderer.hpp"
#include "../OpenGL/OpenGLRenderer.hpp"
#include "../Vulkan/VulkanRenderer.hpp"

Window* Renderer::window = nullptr;

Renderer::~Renderer() {

}

Renderer* Renderer::makeRenderer(BACKEND backend, Window & createdWindow){
    window = &createdWindow;
    // Create and return backend.
    if(backend == BACKEND::OpenGL)
        return new OpenGLRenderer();

    else if(backend == BACKEND::Vulkan)
        return new VulkanRenderer();

    return nullptr;
}
<|endoftext|>


<commit_before>#pragma once
#include "Window.hpp"

/// Controls rendering functionality.
class Renderer {
    public:
        /// The type of backend used to implement the renderer.
        enum class BACKEND{
            OpenGL, ///< OpenGL backend.
            Vulkan ///< Vulkan backend.
        };

        /// Destructor.
        virtual ~Renderer();

        /// Create a new renderer.
        /**
         * @param backend The backend to use for rendering.
         * @param window The GLFW window that will be used for setting up a surface.
         * @return The created renderer.
         */
        static Renderer* makeRenderer(BACKEND backend, Window & window);

        /// Render image to screen.
        virtual void render();
};
<commit_msg>Made render pure virtual.<commit_after>#pragma once
#include "Window.hpp"

/// Controls rendering functionality.
class Renderer {
    public:
        /// The type of backend used to implement the renderer.
        enum class BACKEND{
            OpenGL, ///< OpenGL backend.
            Vulkan ///< Vulkan backend.
        };

        /// Destructor.
        virtual ~Renderer();

        /// Create a new renderer.
        /**
         * @param backend The backend to use for rendering.
         * @param window The GLFW window that will be used for setting up a surface.
         * @return The created renderer.
         */
        static Renderer* makeRenderer(BACKEND backend, Window & window);

        /// Render image to screen.
        virtual void render() = 0;
};
<|endoftext|>