<commit_before>/*
 * DesktopWebPage.cpp
 *
 * Copyright (C) 2009-18 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "DesktopWebPage.hpp"

#include <boost/algorithm/string.hpp>

#include <core/Thread.hpp>

#include <QFileDialog>
#include <QWebEngineSettings>

#include "DesktopInfo.hpp"
#include "DesktopDownloadItemHelper.hpp"
#include "DesktopMainWindow.hpp"
#include "DesktopSatelliteWindow.hpp"
#include "DesktopSecondaryWindow.hpp"
#include "DesktopWebProfile.hpp"
#include "DesktopWindowTracker.hpp"

using namespace rstudio::core;

extern QString sharedSecret;

namespace rstudio {
namespace desktop {

namespace {

WindowTracker s_windowTracker;
std::mutex s_mutex;

// NOTE: This variable is static and hence shared by all of the web pages
// created by RStudio. This is intentional as it's possible that the
// request interceptor from one page will handle a request, and a _different_
// page will handle the new navigation attempt -- so both pages will need
// access to the same data.
std::map<QUrl, int> s_subframes;

void onDownloadRequested(QWebEngineDownloadItem* downloadItem)
{
   // request directory from user
   QString downloadPath = QFileDialog::getSaveFileName(
            nullptr,
            QStringLiteral("Save File"),
            downloadItem->path());

   if (downloadPath.isEmpty())
      return;

   DownloadHelper::manageDownload(downloadItem, downloadPath);
}

} // anonymous namespace

WebPage::WebPage(QUrl baseUrl, QWidget *parent, bool allowExternalNavigate) :
      QWebEnginePage(new WebProfile(baseUrl), parent),
      baseUrl_(baseUrl),
      allowExternalNav_(allowExternalNavigate)
{
   settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
   settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
   settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
   settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
   settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);

   defaultSaveDir_ = QDir::home();
   connect(this, SIGNAL(windowCloseRequested()), SLOT(closeRequested()));
   connect(profile(), &QWebEngineProfile::downloadRequested, onDownloadRequested);
   connect(profile(), &WebProfile::urlIntercepted, this, &WebPage::onUrlIntercepted, Qt::DirectConnection);
}

void WebPage::setBaseUrl(const QUrl& baseUrl)
{
   profile()->setBaseUrl(baseUrl);
   baseUrl_ = baseUrl;
}

void WebPage::activateWindow(QString name)
{
   BrowserWindow* pWindow = s_windowTracker.getWindow(name);
   if (pWindow)
      desktop::raiseAndActivateWindow(pWindow);
}

void WebPage::closeWindow(QString name)
{
   BrowserWindow* pWindow = s_windowTracker.getWindow(name);
   if (pWindow)
      desktop::closeWindow(pWindow);
}

void WebPage::prepareForWindow(const PendingWindow& pendingWindow)
{
   std::lock_guard<std::mutex> lock(s_mutex);

   pendingWindows_.push(pendingWindow);
}

QWebEnginePage* WebPage::createWindow(QWebEnginePage::WebWindowType type)
{
   std::lock_guard<std::mutex> lock(s_mutex);

   QString name;
   bool isSatellite = false;
   bool showToolbar = true;
   bool allowExternalNavigate = false;
   int width = 0;
   int height = 0;
   int x = -1;
   int y = -1;
   MainWindow* pMainWindow = nullptr;
   BrowserWindow* pWindow = nullptr;
   bool show = true;

   // check if this is target="_blank";
   if (type == QWebEnginePage::WebWindowType::WebBrowserTab)
   {
      // QtWebEngine behavior is to open a new browser window and send it the
      // acceptNavigationRequest we use to redirect to system browser; don't want
      // that to be visible
      show = false;
      name = tr("_blank_redirector");

      // check for an existing hidden window
      pWindow = s_windowTracker.getWindow(name);
      if (pWindow)
      {
         return pWindow->webView()->webPage();
      }
   }

   // check if we have a satellite window waiting to come up
   if (!pendingWindows_.empty())
   {
      // retrieve the window
      PendingWindow pendingWindow = pendingWindows_.front();
      pendingWindows_.pop();

      // capture pending window params then clear them (one time only)
      name = pendingWindow.name;
      isSatellite = pendingWindow.isSatellite;
      showToolbar = pendingWindow.showToolbar;
      pMainWindow = pendingWindow.pMainWindow;
      allowExternalNavigate = pendingWindow.allowExternalNavigate;

      // get width and height, and adjust for high DPI
      double dpiZoomScaling = getDpiZoomScaling();
      width = pendingWindow.width * dpiZoomScaling;
      height = pendingWindow.height * dpiZoomScaling;
      x = pendingWindow.x;
      y = pendingWindow.y;

      // check for an existing window of this name
      pWindow = s_windowTracker.getWindow(name);
      if (pWindow)
      {
         // activate the browser then return NULL to indicate
         // we didn't create a new WebView
         desktop::raiseAndActivateWindow(pWindow);
         return nullptr;
      }
   }

   if (isSatellite)
   {
      // create and size
      pWindow = new SatelliteWindow(pMainWindow, name, this);
      pWindow->resize(width, height);

      if (x >= 0 && y >= 0)
      {
         // if the window specified its location, use it
         pWindow->move(x, y);
      }
      else if (name != QString::fromUtf8("pdf"))
      {
         // window location was left for us to determine; try to tile the window
         // (but leave pdf window alone since it is so large)

         // calculate location to move to

         // y always attempts to be 25 pixels above then faults back
         // to 25 pixels below if that would be offscreen
         const int OVERLAP = 25;
         int moveY = pMainWindow->y() - OVERLAP;
         if (moveY < 0)
            moveY = pMainWindow->y() + OVERLAP;

         // x is based on centering over main window
         int moveX = pMainWindow->x() +
               (pMainWindow->width() / 2) -
               (width / 2);

         // perform move
         pWindow->move(moveX, moveY);
      }
   }
   else
   {
      pWindow = new SecondaryWindow(showToolbar, name, baseUrl_, nullptr, this,
                                    allowExternalNavigate);
   }

   // if we have a name set, start tracking this window
   if (!name.isEmpty())
   {
      s_windowTracker.addWindow(name, pWindow);
   }

   // show and return the browser
   if (show)
      pWindow->show();
   return pWindow->webView()->webPage();
}

void WebPage::closeRequested()
{
   // invoked when close is requested via script (i.e. window.close()); honor
   // this request by closing the window in which the view is hosted
   view()->window()->close();
}

void WebPage::onUrlIntercepted(QUrl url, int type)
{
   if (type == QWebEngineUrlRequestInfo::ResourceTypeSubFrame)
   {
      s_subframes[url] = type;
   }
}

bool WebPage::shouldInterruptJavaScript()
{
   return false;
}

void WebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/, const QString& message,
                                       int /*lineNumber*/, const QString& /*sourceID*/)
{
   qDebug() << message;
}

// NOTE: NavigationType is unreliable, as per:
//
//    https://bugreports.qt.io/browse/QTBUG-56805
//
// so we avoid querying it here.
bool WebPage::acceptNavigationRequest(const QUrl &url,
                                      NavigationType /* navType*/,
                                      bool isMainFrame)
{
   if (url.toString() == QStringLiteral("about:blank"))
      return true;

   if (url.scheme() != QStringLiteral("http") &&
       url.scheme() != QStringLiteral("https") &&
       url.scheme() != QStringLiteral("mailto") &&
       url.scheme() != QStringLiteral("data"))
   {
      qDebug() << url.toString();
      return false;
   }

   // determine if this is a local request (handle internally only if local)
   std::string host = url.host().toStdString();
   bool isLocal = host == "localhost" || host == "127.0.0.1" || host == "::1";

   // accept Chrome Developer Tools navigation attempts
#ifndef NDEBUG
   if (isLocal && url.port() == desktopInfo().getChromiumDevtoolsPort())
      return true;
#endif

   // if this appears to be an attempt to load an external page within
   // an iframe, don't load it
   if (!isLocal && s_subframes.count(url))
   {
      s_subframes.erase(url);
      return false;
   }

   if ((baseUrl_.isEmpty() && isLocal) ||
       (url.scheme() == baseUrl_.scheme() &&
        url.host() == baseUrl_.host() &&
        url.port() == baseUrl_.port()))
   {
      return true;
   }
   // allow viewer urls to be handled internally by Qt. note that the client is responsible for
   // ensuring that non-local viewer urls are appropriately sandboxed.
   else if (!viewerUrl().isEmpty() &&
            url.toString().startsWith(viewerUrl()))
   {
      return true;
   }
   // allow shiny dialog urls to be handled internally by Qt
   else if (isLocal && !shinyDialogUrl_.isEmpty() &&
            url.toString().startsWith(shinyDialogUrl_))
   {
      return true;
   }
   else
   {
      bool navigated = false;

      if (allowExternalNav_)
      {
         // if allowing external navigation, follow this (even if a link click)
         return true;
      }
      else if (boost::algorithm::ends_with(host, ".youtube.com") ||
               boost::algorithm::ends_with(host, ".vimeo.com")   ||
               boost::algorithm::ends_with(host, ".c9.ms"))
      {
         return true;
      }
      else
      {
         // when not allowing external navigation, open an external browser
         // to view the URL
         desktop::openUrl(url);
         navigated = true;
      }

      if (!navigated)
         this->view()->window()->deleteLater();

      return false;
   }
}

void WebPage::setViewerUrl(const QString& viewerUrl)
{
   // record about:blank literally
   if (viewerUrl == QString::fromUtf8("about:blank"))
   {
      viewerUrl_ = viewerUrl;
      return;
   }

   // extract the authority (domain and port) from the URL; we'll agree to
   // serve requests for the viewer pane that match this prefix.
   QUrl url(viewerUrl);
   viewerUrl_ = url.scheme() + QString::fromUtf8("://") +
                url.authority() + QString::fromUtf8("/");
}

QString WebPage::viewerUrl()
{
   if (viewerUrl_.isEmpty())
   {
      // if we don't know the viewer URL ourselves but we're a child window, ask our parent
      BrowserWindow *parent = dynamic_cast<BrowserWindow*>(view()->window());
      if (parent != nullptr && parent->opener() != nullptr)
      {
         return parent->opener()->viewerUrl();
      }
   }

   // return our own viewer URL
   return viewerUrl_;
}

void WebPage::setShinyDialogUrl(const QString &shinyDialogUrl)
{
   shinyDialogUrl_ = shinyDialogUrl;
}

void WebPage::triggerAction(WebAction action, bool checked)
{
   QWebEnginePage::triggerAction(action, checked);
}

} // namespace desktop
} // namespace rstudio
<commit_msg>allow iframes loaded from safe hosts<commit_after>/*
 * DesktopWebPage.cpp
 *
 * Copyright (C) 2009-18 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "DesktopWebPage.hpp"

#include <boost/algorithm/string.hpp>

#include <core/Thread.hpp>

#include <QFileDialog>
#include <QWebEngineSettings>

#include "DesktopInfo.hpp"
#include "DesktopDownloadItemHelper.hpp"
#include "DesktopMainWindow.hpp"
#include "DesktopSatelliteWindow.hpp"
#include "DesktopSecondaryWindow.hpp"
#include "DesktopWebProfile.hpp"
#include "DesktopWindowTracker.hpp"

using namespace rstudio::core;

extern QString sharedSecret;

namespace rstudio {
namespace desktop {

namespace {

WindowTracker s_windowTracker;
std::mutex s_mutex;

// NOTE: This variable is static and hence shared by all of the web pages
// created by RStudio. This is intentional as it's possible that the
// request interceptor from one page will handle a request, and a _different_
// page will handle the new navigation attempt -- so both pages will need
// access to the same data.
std::map<QUrl, int> s_subframes;

void onDownloadRequested(QWebEngineDownloadItem* downloadItem)
{
   // request directory from user
   QString downloadPath = QFileDialog::getSaveFileName(
            nullptr,
            QStringLiteral("Save File"),
            downloadItem->path());

   if (downloadPath.isEmpty())
      return;

   DownloadHelper::manageDownload(downloadItem, downloadPath);
}

} // anonymous namespace

WebPage::WebPage(QUrl baseUrl, QWidget *parent, bool allowExternalNavigate) :
      QWebEnginePage(new WebProfile(baseUrl), parent),
      baseUrl_(baseUrl),
      allowExternalNav_(allowExternalNavigate)
{
   settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
   settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
   settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
   settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
   settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);

   defaultSaveDir_ = QDir::home();
   connect(this, SIGNAL(windowCloseRequested()), SLOT(closeRequested()));
   connect(profile(), &QWebEngineProfile::downloadRequested, onDownloadRequested);
   connect(profile(), &WebProfile::urlIntercepted, this, &WebPage::onUrlIntercepted, Qt::DirectConnection);
}

void WebPage::setBaseUrl(const QUrl& baseUrl)
{
   profile()->setBaseUrl(baseUrl);
   baseUrl_ = baseUrl;
}

void WebPage::activateWindow(QString name)
{
   BrowserWindow* pWindow = s_windowTracker.getWindow(name);
   if (pWindow)
      desktop::raiseAndActivateWindow(pWindow);
}

void WebPage::closeWindow(QString name)
{
   BrowserWindow* pWindow = s_windowTracker.getWindow(name);
   if (pWindow)
      desktop::closeWindow(pWindow);
}

void WebPage::prepareForWindow(const PendingWindow& pendingWindow)
{
   std::lock_guard<std::mutex> lock(s_mutex);

   pendingWindows_.push(pendingWindow);
}

QWebEnginePage* WebPage::createWindow(QWebEnginePage::WebWindowType type)
{
   std::lock_guard<std::mutex> lock(s_mutex);

   QString name;
   bool isSatellite = false;
   bool showToolbar = true;
   bool allowExternalNavigate = false;
   int width = 0;
   int height = 0;
   int x = -1;
   int y = -1;
   MainWindow* pMainWindow = nullptr;
   BrowserWindow* pWindow = nullptr;
   bool show = true;

   // check if this is target="_blank";
   if (type == QWebEnginePage::WebWindowType::WebBrowserTab)
   {
      // QtWebEngine behavior is to open a new browser window and send it the
      // acceptNavigationRequest we use to redirect to system browser; don't want
      // that to be visible
      show = false;
      name = tr("_blank_redirector");

      // check for an existing hidden window
      pWindow = s_windowTracker.getWindow(name);
      if (pWindow)
      {
         return pWindow->webView()->webPage();
      }
   }

   // check if we have a satellite window waiting to come up
   if (!pendingWindows_.empty())
   {
      // retrieve the window
      PendingWindow pendingWindow = pendingWindows_.front();
      pendingWindows_.pop();

      // capture pending window params then clear them (one time only)
      name = pendingWindow.name;
      isSatellite = pendingWindow.isSatellite;
      showToolbar = pendingWindow.showToolbar;
      pMainWindow = pendingWindow.pMainWindow;
      allowExternalNavigate = pendingWindow.allowExternalNavigate;

      // get width and height, and adjust for high DPI
      double dpiZoomScaling = getDpiZoomScaling();
      width = pendingWindow.width * dpiZoomScaling;
      height = pendingWindow.height * dpiZoomScaling;
      x = pendingWindow.x;
      y = pendingWindow.y;

      // check for an existing window of this name
      pWindow = s_windowTracker.getWindow(name);
      if (pWindow)
      {
         // activate the browser then return NULL to indicate
         // we didn't create a new WebView
         desktop::raiseAndActivateWindow(pWindow);
         return nullptr;
      }
   }

   if (isSatellite)
   {
      // create and size
      pWindow = new SatelliteWindow(pMainWindow, name, this);
      pWindow->resize(width, height);

      if (x >= 0 && y >= 0)
      {
         // if the window specified its location, use it
         pWindow->move(x, y);
      }
      else if (name != QString::fromUtf8("pdf"))
      {
         // window location was left for us to determine; try to tile the window
         // (but leave pdf window alone since it is so large)

         // calculate location to move to

         // y always attempts to be 25 pixels above then faults back
         // to 25 pixels below if that would be offscreen
         const int OVERLAP = 25;
         int moveY = pMainWindow->y() - OVERLAP;
         if (moveY < 0)
            moveY = pMainWindow->y() + OVERLAP;

         // x is based on centering over main window
         int moveX = pMainWindow->x() +
               (pMainWindow->width() / 2) -
               (width / 2);

         // perform move
         pWindow->move(moveX, moveY);
      }
   }
   else
   {
      pWindow = new SecondaryWindow(showToolbar, name, baseUrl_, nullptr, this,
                                    allowExternalNavigate);
   }

   // if we have a name set, start tracking this window
   if (!name.isEmpty())
   {
      s_windowTracker.addWindow(name, pWindow);
   }

   // show and return the browser
   if (show)
      pWindow->show();
   return pWindow->webView()->webPage();
}

void WebPage::closeRequested()
{
   // invoked when close is requested via script (i.e. window.close()); honor
   // this request by closing the window in which the view is hosted
   view()->window()->close();
}

void WebPage::onUrlIntercepted(QUrl url, int type)
{
   if (type == QWebEngineUrlRequestInfo::ResourceTypeSubFrame)
   {
      s_subframes[url] = type;
   }
}

bool WebPage::shouldInterruptJavaScript()
{
   return false;
}

void WebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/, const QString& message,
                                       int /*lineNumber*/, const QString& /*sourceID*/)
{
   qDebug() << message;
}

namespace {

bool isSafeHost(const std::string& host)
{
   static const std::vector<std::string> whitelist = {
      ".youtube.com",
      ".vimeo.com",
      ".c9.ms"
   };

   for (auto entry : whitelist)
      if (boost::algorithm::ends_with(host, entry))
         return true;

   return false;
}

} // end anonymous namespace

// NOTE: NavigationType is unreliable, as per:
//
//    https://bugreports.qt.io/browse/QTBUG-56805
//
// so we avoid querying it here.
bool WebPage::acceptNavigationRequest(const QUrl &url,
                                      NavigationType /* navType*/,
                                      bool isMainFrame)
{
   if (url.toString() == QStringLiteral("about:blank"))
      return true;

   if (url.scheme() != QStringLiteral("http") &&
       url.scheme() != QStringLiteral("https") &&
       url.scheme() != QStringLiteral("mailto") &&
       url.scheme() != QStringLiteral("data"))
   {
      qDebug() << url.toString();
      return false;
   }

   // determine if this is a local request (handle internally only if local)
   std::string host = url.host().toStdString();
   bool isLocal = host == "localhost" || host == "127.0.0.1" || host == "::1";

   // accept Chrome Developer Tools navigation attempts
#ifndef NDEBUG
   if (isLocal && url.port() == desktopInfo().getChromiumDevtoolsPort())
      return true;
#endif

   // if this appears to be an attempt to load an external page within
   // an iframe, check it's from a safe host
   if (!isLocal && s_subframes.count(url))
   {
      s_subframes.erase(url);
      return isSafeHost(host);
   }

   if ((baseUrl_.isEmpty() && isLocal) ||
       (url.scheme() == baseUrl_.scheme() &&
        url.host() == baseUrl_.host() &&
        url.port() == baseUrl_.port()))
   {
      return true;
   }
   // allow viewer urls to be handled internally by Qt. note that the client is responsible for
   // ensuring that non-local viewer urls are appropriately sandboxed.
   else if (!viewerUrl().isEmpty() &&
            url.toString().startsWith(viewerUrl()))
   {
      return true;
   }
   // allow shiny dialog urls to be handled internally by Qt
   else if (isLocal && !shinyDialogUrl_.isEmpty() &&
            url.toString().startsWith(shinyDialogUrl_))
   {
      return true;
   }
   else
   {
      bool navigated = false;

      if (allowExternalNav_)
      {
         // if allowing external navigation, follow this (even if a link click)
         return true;
      }
      else if (isSafeHost(host))
      {
         return true;
      }
      else
      {
         // when not allowing external navigation, open an external browser
         // to view the URL
         desktop::openUrl(url);
         navigated = true;
      }

      if (!navigated)
         this->view()->window()->deleteLater();

      return false;
   }
}

void WebPage::setViewerUrl(const QString& viewerUrl)
{
   // record about:blank literally
   if (viewerUrl == QString::fromUtf8("about:blank"))
   {
      viewerUrl_ = viewerUrl;
      return;
   }

   // extract the authority (domain and port) from the URL; we'll agree to
   // serve requests for the viewer pane that match this prefix.
   QUrl url(viewerUrl);
   viewerUrl_ = url.scheme() + QString::fromUtf8("://") +
                url.authority() + QString::fromUtf8("/");
}

QString WebPage::viewerUrl()
{
   if (viewerUrl_.isEmpty())
   {
      // if we don't know the viewer URL ourselves but we're a child window, ask our parent
      BrowserWindow *parent = dynamic_cast<BrowserWindow*>(view()->window());
      if (parent != nullptr && parent->opener() != nullptr)
      {
         return parent->opener()->viewerUrl();
      }
   }

   // return our own viewer URL
   return viewerUrl_;
}

void WebPage::setShinyDialogUrl(const QString &shinyDialogUrl)
{
   shinyDialogUrl_ = shinyDialogUrl;
}

void WebPage::triggerAction(WebAction action, bool checked)
{
   QWebEnginePage::triggerAction(action, checked);
}

} // namespace desktop
} // namespace rstudio
<|endoftext|>


<commit_before>/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic startup code for a JUCE application.

  ==============================================================================
*/

#include "../JuceLibraryCode/JuceHeader.h"
#include "GraphicsWindow.h"
#include "LocalSynth.h"
#include "Terminal.h"
#include "ForthThread.h"

#define HMSL_VERSION "v0.5.3"

//==============================================================================
class ProtoHMSLApplication  : public JUCEApplication
{
public:
    //==============================================================================
    ProtoHMSLApplication() {}

    const String getApplicationName() override       { return ProjectInfo::projectName; }
    const String getApplicationVersion() override    { return ProjectInfo::versionString; }
    bool moreThanOneInstanceAllowed() override       { return true; }

    //==============================================================================
    void initialise (const String& commandLine) override
    {
        // This method is where you should put your application's initialisation code..
        mTerminalWindow.reset (new TerminalWindow (getApplicationName()));

        mForthThread.reset(new ForthThread());
        mForthThread->startThread();
    }

    void shutdown() override
    {
        // TODO needed?
        mTerminalWindow->requestClose();
        mForthThread->signalThreadShouldExit();
        mForthThread->stopThread(1000);
    }

    //==============================================================================
    void systemRequestedQuit() override
    {
        mTerminalWindow->requestClose();
        mForthThread->waitForThreadToExit(500);
        // This is called when the app is being asked to quit: you can ignore this
        // request and let the app carry on running, or call quit() to allow the app to close.
        quit();
    }

    void anotherInstanceStarted (const String& commandLine) override
    {
        // When another instance of the app is launched while this one is running,
        // this method is invoked, and the commandLine parameter tells you what
        // the other instance's command-line arguments were.
    }

    //==============================================================================
    /*
     This class implements the desktop window that contains an instance of
     our TerminalComponent class.
     */
    class TerminalWindow    : public DocumentWindow
    {
    public:
        TerminalWindow (String name)  : DocumentWindow (name + (" " HMSL_VERSION),
                                                    Desktop::getInstance().getDefaultLookAndFeel()
                                                    .findColour (ResizableWindow::backgroundColourId),
                                                    DocumentWindow::allButtons)
        {
            setUsingNativeTitleBar (true);
            mTerminal.reset(new Terminal());
            setContentOwned(mTerminal.get(), true);

#if JUCE_IOS || JUCE_ANDROID
            setFullScreen (true);
#else
            setResizable (true, true);
            centreWithSize (getWidth(), getHeight());
#endif

            setVisible (true);

        }

        void requestClose() {
            mTerminal->requestClose();
            usleep(50 * 1000); // wait for Forth to get the message
        }

        void closeButtonPressed() override
        {
            requestClose();

            // This is called when the user tries to close this window. Here, we'll just
            // ask the app to quit when this happens, but you can change this to do
            // whatever you need.
            JUCEApplication::getInstance()->systemRequestedQuit();
        }

        /* Note: Be careful if you override any DocumentWindow methods - the base
         class uses a lot of them, so by overriding you might break its functionality.
         It's best to do all your work in your content component instead, but if
         you really have to override any DocumentWindow methods, make sure your
         subclass also calls the superclass's method.
         */

    private:
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TerminalWindow)

        std::unique_ptr<Terminal>   mTerminal;
    };

private:
    std::unique_ptr<TerminalWindow> mTerminalWindow;
    std::unique_ptr<ForthThread>    mForthThread;
};

//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (ProtoHMSLApplication)
<commit_msg>version bump to 0.5.5<commit_after>/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic startup code for a JUCE application.

  ==============================================================================
*/

#include "../JuceLibraryCode/JuceHeader.h"
#include "GraphicsWindow.h"
#include "LocalSynth.h"
#include "Terminal.h"
#include "ForthThread.h"

#define HMSL_VERSION "v0.5.5"

//==============================================================================
class ProtoHMSLApplication  : public JUCEApplication
{
public:
    //==============================================================================
    ProtoHMSLApplication() {}

    const String getApplicationName() override       { return ProjectInfo::projectName; }
    const String getApplicationVersion() override    { return ProjectInfo::versionString; }
    bool moreThanOneInstanceAllowed() override       { return true; }

    //==============================================================================
    void initialise (const String& commandLine) override
    {
        // This method is where you should put your application's initialisation code..
        mTerminalWindow.reset (new TerminalWindow (getApplicationName()));

        mForthThread.reset(new ForthThread());
        mForthThread->startThread();
    }

    void shutdown() override
    {
        // TODO needed?
        mTerminalWindow->requestClose();
        mForthThread->signalThreadShouldExit();
        mForthThread->stopThread(1000);
    }

    //==============================================================================
    void systemRequestedQuit() override
    {
        mTerminalWindow->requestClose();
        mForthThread->waitForThreadToExit(500);
        // This is called when the app is being asked to quit: you can ignore this
        // request and let the app carry on running, or call quit() to allow the app to close.
        quit();
    }

    void anotherInstanceStarted (const String& commandLine) override
    {
        // When another instance of the app is launched while this one is running,
        // this method is invoked, and the commandLine parameter tells you what
        // the other instance's command-line arguments were.
    }

    //==============================================================================
    /*
     This class implements the desktop window that contains an instance of
     our TerminalComponent class.
     */
    class TerminalWindow    : public DocumentWindow
    {
    public:
        TerminalWindow (String name)  : DocumentWindow (name + (" " HMSL_VERSION),
                                                    Desktop::getInstance().getDefaultLookAndFeel()
                                                    .findColour (ResizableWindow::backgroundColourId),
                                                    DocumentWindow::allButtons)
        {
            setUsingNativeTitleBar (true);
            mTerminal.reset(new Terminal());
            setContentOwned(mTerminal.get(), true);

#if JUCE_IOS || JUCE_ANDROID
            setFullScreen (true);
#else
            setResizable (true, true);
            centreWithSize (getWidth(), getHeight());
#endif

            setVisible (true);

        }

        void requestClose() {
            mTerminal->requestClose();
            usleep(50 * 1000); // wait for Forth to get the message
        }

        void closeButtonPressed() override
        {
            requestClose();

            // This is called when the user tries to close this window. Here, we'll just
            // ask the app to quit when this happens, but you can change this to do
            // whatever you need.
            JUCEApplication::getInstance()->systemRequestedQuit();
        }

        /* Note: Be careful if you override any DocumentWindow methods - the base
         class uses a lot of them, so by overriding you might break its functionality.
         It's best to do all your work in your content component instead, but if
         you really have to override any DocumentWindow methods, make sure your
         subclass also calls the superclass's method.
         */

    private:
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TerminalWindow)

        std::unique_ptr<Terminal>   mTerminal;
    };

private:
    std::unique_ptr<TerminalWindow> mTerminalWindow;
    std::unique_ptr<ForthThread>    mForthThread;
};

//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (ProtoHMSLApplication)
<|endoftext|>


<commit_before>#include "include/moduler.hpp"
#include "include/imoduleexport.hpp"
#include <loader/include/loader.hpp>
#include <logger/include/logger.hpp>
#include <sstream>

namespace moduler
{
	static Moduler *modulerInstance{ nullptr };

	class ModulerPrivate
	{
	public:
		loader::Loader *loaderInstance{ nullptr };

		ModulerPrivate()
			: loaderInstance{ loader::createLoader() } {}

		~ModulerPrivate() { loader::destroyLoader(); }
	};

	Moduler::Moduler()
	{
		m_private = new ModulerPrivate;
	}

	Moduler::~Moduler()
	{
		delete m_private;
	}

	typedef moduler::IModule * (*createModuleFunc)();
	typedef void (*deleteModuleFunc)(moduler::IModule *);

	IModule *Moduler::loadModule(const char * fileName)
	{
		LOG_DEBUG_STR("Going to open " << fileName);
		auto *moduleData(m_private->loaderInstance->loadModule(fileName));
		if (moduleData) {
			LOG_DEBUG("Object file loaded");
			auto creatorFunc = static_cast<createModuleFunc>(m_private->loaderInstance->loadMethod(fileName, CREATE_MODULE_FUNC_NAME_STR));
			auto deleterFunc = static_cast<deleteModuleFunc>(m_private->loaderInstance->loadMethod(fileName, DELETE_MODULE_FUNC_NAME_STR));
			if (creatorFunc && deleterFunc) {
				IModule *loadedModule = creatorFunc();
				return loadedModule;
			}
			else {
				LOG_WARNING("Cannot read method createModule()");
			}
		}
		return nullptr;
	}

	Moduler *createModuler ()
	{
		if (!modulerInstance) {
			LOG_DEBUG("Creating moduler...");
			modulerInstance = new Moduler;
			LOG_DEBUG("Moduler created");
		}
		else {
			LOG_DEBUG("Moduler already reated");
		}

		return modulerInstance;
	}

	void destroyModuler()
	{
		if (modulerInstance)
		{
			LOG_DEBUG("Destroying moduler...");
			delete modulerInstance;
			modulerInstance = nullptr;
			LOG_DEBUG("Moduler destroyed");
		}
	}
}
<commit_msg>Reading getModule func<commit_after>#include "include/moduler.hpp"
#include "include/imoduleexport.hpp"
#include <loader/include/loader.hpp>
#include <logger/include/logger.hpp>
#include <sstream>

namespace moduler
{
	static Moduler *modulerInstance{ nullptr };

	class ModulerPrivate
	{
	public:
		loader::Loader *loaderInstance{ nullptr };

		ModulerPrivate()
			: loaderInstance{ loader::createLoader() } {}

		~ModulerPrivate() { loader::destroyLoader(); }
	};

	Moduler::Moduler()
	{
		m_private = new ModulerPrivate;
	}

	Moduler::~Moduler()
	{
		delete m_private;
	}

	using CreateModuleFunc  = bool (*)();
	using GetModuleFunc = moduler::IModule * (*)();
	using DeleteModuleFunc = bool(*)();

	IModule *Moduler::loadModule(const char * fileName)
	{
		LOG_DEBUG_STR("Going to open " << fileName);
		auto *moduleData(m_private->loaderInstance->loadModule(fileName));
		if (moduleData) {
			LOG_DEBUG("Object file loaded");
			auto createModuleFunc = static_cast<CreateModuleFunc>(m_private->loaderInstance->loadMethod(fileName, CREATE_MODULE_FUNC_NAME_STR));
			auto getModuleFunc = static_cast<GetModuleFunc>(m_private->loaderInstance->loadMethod(fileName, GET_MODULE_FUNC_NAME_STR));
			auto deleteModuleFunc = static_cast<DeleteModuleFunc>(m_private->loaderInstance->loadMethod(fileName, DELETE_MODULE_FUNC_NAME_STR));
			if (createModuleFunc && getModuleFunc && deleteModuleFunc) {
				createModuleFunc();
				IModule *loadedModule = getModuleFunc();
				return loadedModule;
			}
			else {
				LOG_WARNING("Cannot read method createModule()");
			}
		}
		return nullptr;
	}

	Moduler *createModuler ()
	{
		if (!modulerInstance) {
			LOG_DEBUG("Creating moduler...");
			modulerInstance = new Moduler;
			LOG_DEBUG("Moduler created");
		}
		else {
			LOG_DEBUG("Moduler already reated");
		}

		return modulerInstance;
	}

	void destroyModuler()
	{
		if (modulerInstance)
		{
			LOG_DEBUG("Destroying moduler...");
			delete modulerInstance;
			modulerInstance = nullptr;
			LOG_DEBUG("Moduler destroyed");
		}
	}
}
<|endoftext|>


<commit_before>#include "VaArg.h"

#include <vector>

#include "../../../Arch/Arch.h"
#include "../../../Function/Function.h"
#include "../../../Node/Node.h"
#include "../../../ParseResult/ParseResult.h"
#include "../../../Units/Units.h"
#include "../../../Operation/Alignmentof/Alignmentof.h"
#include "../../../Operation/Sizeof/Sizeof.h"
#include "../../../llvmUtils/llvmUtils.h"
#include "../../../llvm_Function.h"
#include "../../Type/Type.h"
#include "../Inst/Inst.h"

namespace dale {
llvm::Function *va_arg_aarch64 = NULL;

/*
(See the AMD64 ABI draft of 13/01/2010 for more information; most of
the below is verbatim from there, and the document details the
algorithm to use, which in turn is the basis of parseVaArg64.)

va_list on x86-64 looks like so:

typedef struct {
    unsigned int gp_offset;
    unsigned int fp_offset;
    void *overflow_arg_area;
    void *reg_save_area;
} va_list[1];

reg_save_area: points to the start of the register save area.

overflow_arg_area: points to the address of the first argument passed
on the stack, if any. When an argument is fetched, it should be
updated to point to the start of the next argument on the stack.

gp_offset: holds the offset in bytes from reg_save_area to the place
where the next available general purpose argument register is saved.
In case all argument registers have been exhausted, it is set to the
value 48 (6 * 8).

fp_offset: holds the offset in bytes from reg_save_area to the place
where the next available floating point argument register is saved. In
case all argument registers have been exhausted, it is set to the
value 304 ((6 * 8) + (16 * 16)).

Algorithm:

    1.  Determine whether 'type' may be passed in the
        registers. If not, go to 7.
    2.  Compute 'num_gp' to hold the number of general
        purpose registers needed to pass 'type' and
        'num_fp' to hold the number of floating point
        registers needed.
    3.  Verify whether arguments fit into registers. In
        the event that:

        gp_offset > (48 - (num_gp * 8))
            ||
        fp_offset > (304 - (num_fp * 16))

        go to 7.

    4.  Fetch 'type' from 'reg_save_area' with an
        offset of 'gp_offset' and/or 'fp_offset'. This
        may require copying to a temporary location in
        case the parameter is passed in different
        register classes or requires an alignment of
        greater than 8 for general purpose registers
        and 16 for XMM registers.
    5.  Set:

        gp_offset += num_gp * 8
        fp_offset += num_fp * 16

    6.  Return the fetched type.

    7.  Align 'overflow_arg_area' upwards to a 16-byte
        boundary if alignment needed by 'type' exceeds
        8-byte boundary.
    8.  Fetch 'type' from 'overflow_arg_area'.
    9.  Set:

        overflow_arg_area += sizeof(type)

    10. Align overflow_arg_area upwards to an 8-byte
        boundary.
    11. Return the fetched type.
*/

bool parseVaArg64(Units *units, Function *fn, Type *type,
                  llvm::Type *llvm_type, ParseResult *arglist_pr,
                  ParseResult *pr) {
    Context *ctx = units->top()->ctx;
    llvm::IRBuilder<> builder(arglist_pr->block);

    /* 1 to 2. */

    llvm::Value *pvlstr = llvm::cast<llvm::Value>(builder.CreateBitCast(
        arglist_pr->getValue(ctx),
        llvm::PointerType::getUnqual(ctx->getStruct("va-list")->type)));

    std::vector<llvm::Value *> indices_gp_offset;
    STL::push_back2(&indices_gp_offset, ctx->nt->getLLVMZero(),
                    ctx->nt->getLLVMZero());

    std::vector<llvm::Value *> indices_overflow_arg_area;
    STL::push_back2(&indices_overflow_arg_area, ctx->nt->getLLVMZero(),
                    ctx->nt->getNativeInt(2));

    std::vector<llvm::Value *> indices_reg_save_area;
    STL::push_back2(&indices_reg_save_area, ctx->nt->getLLVMZero(),
                    ctx->nt->getNativeInt(3));

    llvm::Value *ptr_gpo = builder.CreateGEP(
        pvlstr, llvm::ArrayRef<llvm::Value *>(indices_gp_offset));

    llvm::Value *gpo =
        llvm::cast<llvm::Value>(builder.CreateLoad(ptr_gpo));

    llvm::Value *ptr_oaa = builder.CreateGEP(
        pvlstr,
        llvm::ArrayRef<llvm::Value *>(indices_overflow_arg_area));

    llvm::Value *oaa =
        llvm::cast<llvm::Value>(builder.CreateLoad(ptr_oaa));

    llvm::Value *ptr_rsa = builder.CreateGEP(
        pvlstr, llvm::ArrayRef<llvm::Value *>(indices_reg_save_area));

    llvm::Value *rsa =
        llvm::cast<llvm::Value>(builder.CreateLoad(ptr_rsa));

    /* Verify whether arguments fit into registers.  For the time
     * being, this is so if gp_offset > 40. */

    llvm::BasicBlock *then_block = llvm::BasicBlock::Create(
        *getContext(), "then", fn->llvm_function);
    llvm::BasicBlock *else_block = llvm::BasicBlock::Create(
        *getContext(), "else", fn->llvm_function);

    llvm::Value *cond = builder.CreateICmpUGT(
        gpo, llvm::ConstantInt::get(ctx->nt->getNativeUIntType(), 40));

    builder.CreateCondBr(cond, then_block, else_block);

    llvm::IRBuilder<> builder_then(then_block);
    llvm::IRBuilder<> builder_else(else_block);

    /* Then component: 4-6. */

    std::vector<llvm::Value *> indices_po1;
    indices_po1.push_back(
        llvm::ConstantInt::get(ctx->nt->getNativeUIntType(), 0));

    llvm::Value *then_ptr_obj = builder_then.CreateGEP(
        oaa, llvm::ArrayRef<llvm::Value *>(indices_po1));

    llvm::Value *then_value =
        builder_then.CreateLoad(builder_then.CreateBitCast(
            then_ptr_obj, llvm::PointerType::getUnqual(llvm_type)));

    builder_then.CreateStore(
        builder_then.CreateIntToPtr(
            builder_then.CreateAdd(
                builder_then.CreatePtrToInt(
                    oaa, llvm::Type::getInt64Ty(*getContext())),
                llvm::ConstantInt::get(
                    llvm::Type::getInt64Ty(*getContext()), 8)),
            llvm::PointerType::getUnqual(
                llvm::Type::getInt8Ty(*getContext()))),
        ptr_oaa);

    /* Else component: 7 onwards. */

    std::vector<llvm::Value *> indices_po2;
    indices_po2.push_back(gpo);

    llvm::Value *ptr_obj = builder_else.CreateGEP(
        rsa, llvm::ArrayRef<llvm::Value *>(indices_po2));

    llvm::Value *else_value =
        builder_else.CreateLoad(builder_else.CreateBitCast(
            ptr_obj, llvm::PointerType::getUnqual(llvm_type)));

    builder_else.CreateStore(
        builder_else.CreateAdd(gpo, ctx->nt->getNativeInt(8)), ptr_gpo);

    /* Final parts. */

    llvm::BasicBlock *done_block = llvm::BasicBlock::Create(
        *getContext(), "va_done", fn->llvm_function);

    builder_then.CreateBr(done_block);
    builder_else.CreateBr(done_block);

    llvm::IRBuilder<> builder_done(done_block);

    llvm::PHINode *pn = builder_done.CreatePHI(llvm_type, 0);

    pn->addIncoming(then_value, then_block);
    pn->addIncoming(else_value, else_block);

    pr->set(done_block, type, llvm::cast<llvm::Value>(pn));

    return true;
}

bool FormProcVaArgParse(Units *units, Function *fn,
                        llvm::BasicBlock *block, Node *node,
                        bool get_address, bool prefixed_with_core,
                        ParseResult *pr) {
    Context *ctx = units->top()->ctx;

    if (!ctx->er->assertArgNums("va-arg", node, 2, 2)) {
        return false;
    }

    std::vector<Node *> *lst = node->list;

    ParseResult arglist_pr;
    bool res = FormProcInstParse(units, fn, block, (*lst)[1], false,
                                 false, NULL, &arglist_pr);
    if (!res) {
        return false;
    }

    Type *type = FormTypeParse(units, (*lst)[2], false, false);
    if (!type) {
        return false;
    }

    llvm::Type *llvm_type = ctx->toLLVMType(type, NULL, false, false);
    if (!llvm_type) {
        return false;
    }

    int arch = units->top()->arch;
    if (arch == Arch::X86_64) {
        return parseVaArg64(units, fn, type, llvm_type, &arglist_pr,
                            pr);
    } else if (arch == Arch::X86) {
        /* Use the default va-arg intrinsic implementation. */
        llvm::IRBuilder<> builder(block);
        llvm::Value *res2 =
            builder.CreateVAArg(arglist_pr.getValue(ctx), llvm_type);
        pr->set(arglist_pr.block, type, res2);
        return true;
    } else if (arch == Arch::AARCH64) {
        if (!va_arg_aarch64) {
            va_arg_aarch64 =
                ctx->getFunction("va-arg-aarch64", NULL, NULL, 0)->llvm_function;
            if (!va_arg_aarch64) {
                fprintf(stderr, "va-arg-aarch64 not found!");
                abort();
            }
        }

        /* Determine the type enum for the type. */
        int type_enum = 0;
        Struct *st = NULL;
        if (type->is_array
                && ((type->array_type->base_type == BaseType::Double) ||
                    (type->array_type->base_type == BaseType::Float))
                && (type->array_size <= 4)) {
            type_enum = 1;
        }
        if (type->struct_name.size() > 0) {
            st = ctx->getStruct(type);
            std::vector<Type *> *member_types = &(st->member_types);
            std::set<int> base_types;
            for (std::vector<Type *>::iterator b = member_types->begin(),
                                               e = member_types->end();
                    b != e;
                    ++b) {
                base_types.insert((*b)->base_type);
            }
            if ((member_types->size() <= 4)
                    && (base_types.size() == 1)
                    && ((base_types.find(BaseType::Double) != base_types.end()) ||
                        (base_types.find(BaseType::Float)  != base_types.end()))) {
                type_enum = 1;
            }
        }
        if ((type->base_type == BaseType::Double) ||
                (type->base_type == BaseType::Float)) {
            type_enum = 2;
        }

        /* Determine the alignment and size of the type. */
        ParseResult alignment_pr;
        bool res1 = Operation::Alignmentof(ctx, block, type, &alignment_pr);
        if (!res1) {
            return false;
        }
        int sizeof_type = Operation::SizeofGet(units->top(), type);
        if (!sizeof_type) {
            return false;
        }

        /* If the type enum is 1, then pass in the retval as the buffer,
         * plus the number of elements in the type. */
        llvm::IRBuilder<> builder(arglist_pr.block);
        llvm::Value *buffer = NULL;
        llvm::Value *elements = NULL;
        if (type_enum != 1) {
            buffer = getNullPointer(ctx->toLLVMType(
                                        ctx->tr->getPointerType(
                                            ctx->tr->type_void
                                        ), node, true));
            elements = ctx->nt->getLLVMZero();
        } else {
            if (pr->retval) {
                buffer = pr->retval;
                pr->retval_used = 1;
                pr->retval_type = ctx->tr->getPointerType(type);
            } else {
                buffer = builder.CreateAlloca(ctx->toLLVMType(type, node, true));
            }
            elements =
                ctx->nt->getNativeInt(
                    type->is_array ? type->array_size
                                : st->member_types.size()
                );
        }

        /* Call the helper function. */
        std::vector<llvm::Value *> call_args;
        call_args.push_back(arglist_pr.getValue(ctx));
        call_args.push_back(ctx->nt->getNativeInt(type_enum));
        call_args.push_back(alignment_pr.getValue(ctx));
        call_args.push_back(llvm::ConstantInt::get(ctx->nt->getNativeSizeType(), sizeof_type));
        call_args.push_back(buffer);
        call_args.push_back(elements);
        llvm::Value *ret =
            builder.CreateCall(va_arg_aarch64,
                                llvm::ArrayRef<llvm::Value *>(call_args));

        /* If the type enum is 1, nothing else needs to be done.
         * Otherwise, cast/copy the value for the return. */
        if (type_enum == 1) {
            if (pr->retval) {
                pr->set(arglist_pr.block, type, ret);
            } else {
                llvm::Value *bc =
                    builder.CreateBitCast(ret,
                                        ctx->toLLVMType(
                                            ctx->tr->getPointerType(type),
                                            node, true));
                llvm::Value *loaded =
                    builder.CreateLoad(bc);
                pr->set(arglist_pr.block, type, loaded);
            }
            return true;
        } else {
            llvm::Value *bc =
                builder.CreateBitCast(ret,
                                      ctx->toLLVMType(
                                        ctx->tr->getPointerType(type),
                                        node, true));
            llvm::Value *loaded =
                builder.CreateLoad(bc);
            if (pr->retval) {
                builder.CreateStore(loaded, pr->retval);
                pr->retval_used = 1;
                pr->retval_type = ctx->tr->getPointerType(type);
                pr->set(arglist_pr.block, type, ret);
            } else {
                pr->set(arglist_pr.block, type, loaded);
            }
            return true;
        }
    } else {
        fprintf(stderr, "Unsupported architecture for va-arg\n");
        abort();
    }
}
}
<commit_msg>Missed va-arg casting.<commit_after>#include "VaArg.h"

#include <vector>

#include "../../../Arch/Arch.h"
#include "../../../Function/Function.h"
#include "../../../Node/Node.h"
#include "../../../ParseResult/ParseResult.h"
#include "../../../Units/Units.h"
#include "../../../Operation/Alignmentof/Alignmentof.h"
#include "../../../Operation/Sizeof/Sizeof.h"
#include "../../../llvmUtils/llvmUtils.h"
#include "../../../llvm_Function.h"
#include "../../Type/Type.h"
#include "../Inst/Inst.h"

namespace dale {
llvm::Function *va_arg_aarch64 = NULL;

/*
(See the AMD64 ABI draft of 13/01/2010 for more information; most of
the below is verbatim from there, and the document details the
algorithm to use, which in turn is the basis of parseVaArg64.)

va_list on x86-64 looks like so:

typedef struct {
    unsigned int gp_offset;
    unsigned int fp_offset;
    void *overflow_arg_area;
    void *reg_save_area;
} va_list[1];

reg_save_area: points to the start of the register save area.

overflow_arg_area: points to the address of the first argument passed
on the stack, if any. When an argument is fetched, it should be
updated to point to the start of the next argument on the stack.

gp_offset: holds the offset in bytes from reg_save_area to the place
where the next available general purpose argument register is saved.
In case all argument registers have been exhausted, it is set to the
value 48 (6 * 8).

fp_offset: holds the offset in bytes from reg_save_area to the place
where the next available floating point argument register is saved. In
case all argument registers have been exhausted, it is set to the
value 304 ((6 * 8) + (16 * 16)).

Algorithm:

    1.  Determine whether 'type' may be passed in the
        registers. If not, go to 7.
    2.  Compute 'num_gp' to hold the number of general
        purpose registers needed to pass 'type' and
        'num_fp' to hold the number of floating point
        registers needed.
    3.  Verify whether arguments fit into registers. In
        the event that:

        gp_offset > (48 - (num_gp * 8))
            ||
        fp_offset > (304 - (num_fp * 16))

        go to 7.

    4.  Fetch 'type' from 'reg_save_area' with an
        offset of 'gp_offset' and/or 'fp_offset'. This
        may require copying to a temporary location in
        case the parameter is passed in different
        register classes or requires an alignment of
        greater than 8 for general purpose registers
        and 16 for XMM registers.
    5.  Set:

        gp_offset += num_gp * 8
        fp_offset += num_fp * 16

    6.  Return the fetched type.

    7.  Align 'overflow_arg_area' upwards to a 16-byte
        boundary if alignment needed by 'type' exceeds
        8-byte boundary.
    8.  Fetch 'type' from 'overflow_arg_area'.
    9.  Set:

        overflow_arg_area += sizeof(type)

    10. Align overflow_arg_area upwards to an 8-byte
        boundary.
    11. Return the fetched type.
*/

bool parseVaArg64(Units *units, Function *fn, Type *type,
                  llvm::Type *llvm_type, ParseResult *arglist_pr,
                  ParseResult *pr) {
    Context *ctx = units->top()->ctx;
    llvm::IRBuilder<> builder(arglist_pr->block);

    /* 1 to 2. */

    llvm::Value *pvlstr = llvm::cast<llvm::Value>(builder.CreateBitCast(
        arglist_pr->getValue(ctx),
        llvm::PointerType::getUnqual(ctx->getStruct("va-list")->type)));

    std::vector<llvm::Value *> indices_gp_offset;
    STL::push_back2(&indices_gp_offset, ctx->nt->getLLVMZero(),
                    ctx->nt->getLLVMZero());

    std::vector<llvm::Value *> indices_overflow_arg_area;
    STL::push_back2(&indices_overflow_arg_area, ctx->nt->getLLVMZero(),
                    ctx->nt->getNativeInt(2));

    std::vector<llvm::Value *> indices_reg_save_area;
    STL::push_back2(&indices_reg_save_area, ctx->nt->getLLVMZero(),
                    ctx->nt->getNativeInt(3));

    llvm::Value *ptr_gpo = builder.CreateGEP(
        pvlstr, llvm::ArrayRef<llvm::Value *>(indices_gp_offset));

    llvm::Value *gpo =
        llvm::cast<llvm::Value>(builder.CreateLoad(ptr_gpo));

    llvm::Value *ptr_oaa = builder.CreateGEP(
        pvlstr,
        llvm::ArrayRef<llvm::Value *>(indices_overflow_arg_area));

    llvm::Value *oaa =
        llvm::cast<llvm::Value>(builder.CreateLoad(ptr_oaa));

    llvm::Value *ptr_rsa = builder.CreateGEP(
        pvlstr, llvm::ArrayRef<llvm::Value *>(indices_reg_save_area));

    llvm::Value *rsa =
        llvm::cast<llvm::Value>(builder.CreateLoad(ptr_rsa));

    /* Verify whether arguments fit into registers.  For the time
     * being, this is so if gp_offset > 40. */

    llvm::BasicBlock *then_block = llvm::BasicBlock::Create(
        *getContext(), "then", fn->llvm_function);
    llvm::BasicBlock *else_block = llvm::BasicBlock::Create(
        *getContext(), "else", fn->llvm_function);

    llvm::Value *cond = builder.CreateICmpUGT(
        gpo, llvm::ConstantInt::get(ctx->nt->getNativeUIntType(), 40));

    builder.CreateCondBr(cond, then_block, else_block);

    llvm::IRBuilder<> builder_then(then_block);
    llvm::IRBuilder<> builder_else(else_block);

    /* Then component: 4-6. */

    std::vector<llvm::Value *> indices_po1;
    indices_po1.push_back(
        llvm::ConstantInt::get(ctx->nt->getNativeUIntType(), 0));

    llvm::Value *then_ptr_obj = builder_then.CreateGEP(
        oaa, llvm::ArrayRef<llvm::Value *>(indices_po1));

    llvm::Value *then_value =
        builder_then.CreateLoad(builder_then.CreateBitCast(
            then_ptr_obj, llvm::PointerType::getUnqual(llvm_type)));

    builder_then.CreateStore(
        builder_then.CreateIntToPtr(
            builder_then.CreateAdd(
                builder_then.CreatePtrToInt(
                    oaa, llvm::Type::getInt64Ty(*getContext())),
                llvm::ConstantInt::get(
                    llvm::Type::getInt64Ty(*getContext()), 8)),
            llvm::PointerType::getUnqual(
                llvm::Type::getInt8Ty(*getContext()))),
        ptr_oaa);

    /* Else component: 7 onwards. */

    std::vector<llvm::Value *> indices_po2;
    indices_po2.push_back(gpo);

    llvm::Value *ptr_obj = builder_else.CreateGEP(
        rsa, llvm::ArrayRef<llvm::Value *>(indices_po2));

    llvm::Value *else_value =
        builder_else.CreateLoad(builder_else.CreateBitCast(
            ptr_obj, llvm::PointerType::getUnqual(llvm_type)));

    builder_else.CreateStore(
        builder_else.CreateAdd(gpo, ctx->nt->getNativeInt(8)), ptr_gpo);

    /* Final parts. */

    llvm::BasicBlock *done_block = llvm::BasicBlock::Create(
        *getContext(), "va_done", fn->llvm_function);

    builder_then.CreateBr(done_block);
    builder_else.CreateBr(done_block);

    llvm::IRBuilder<> builder_done(done_block);

    llvm::PHINode *pn = builder_done.CreatePHI(llvm_type, 0);

    pn->addIncoming(then_value, then_block);
    pn->addIncoming(else_value, else_block);

    pr->set(done_block, type, llvm::cast<llvm::Value>(pn));

    return true;
}

bool FormProcVaArgParse(Units *units, Function *fn,
                        llvm::BasicBlock *block, Node *node,
                        bool get_address, bool prefixed_with_core,
                        ParseResult *pr) {
    Context *ctx = units->top()->ctx;

    if (!ctx->er->assertArgNums("va-arg", node, 2, 2)) {
        return false;
    }

    std::vector<Node *> *lst = node->list;

    ParseResult arglist_pr;
    bool res = FormProcInstParse(units, fn, block, (*lst)[1], false,
                                 false, NULL, &arglist_pr);
    if (!res) {
        return false;
    }

    Type *type = FormTypeParse(units, (*lst)[2], false, false);
    if (!type) {
        return false;
    }

    llvm::Type *llvm_type = ctx->toLLVMType(type, NULL, false, false);
    if (!llvm_type) {
        return false;
    }

    int arch = units->top()->arch;
    if (arch == Arch::X86_64) {
        return parseVaArg64(units, fn, type, llvm_type, &arglist_pr,
                            pr);
    } else if (arch == Arch::X86) {
        /* Use the default va-arg intrinsic implementation. */
        llvm::IRBuilder<> builder(block);
        llvm::Value *res2 =
            builder.CreateVAArg(arglist_pr.getValue(ctx), llvm_type);
        pr->set(arglist_pr.block, type, res2);
        return true;
    } else if (arch == Arch::AARCH64) {
        if (!va_arg_aarch64) {
            va_arg_aarch64 =
                ctx->getFunction("va-arg-aarch64", NULL, NULL, 0)->llvm_function;
            if (!va_arg_aarch64) {
                fprintf(stderr, "va-arg-aarch64 not found!");
                abort();
            }
        }

        /* Determine the type enum for the type. */
        int type_enum = 0;
        Struct *st = NULL;
        if (type->is_array
                && ((type->array_type->base_type == BaseType::Double) ||
                    (type->array_type->base_type == BaseType::Float))
                && (type->array_size <= 4)) {
            type_enum = 1;
        }
        if (type->struct_name.size() > 0) {
            st = ctx->getStruct(type);
            std::vector<Type *> *member_types = &(st->member_types);
            std::set<int> base_types;
            for (std::vector<Type *>::iterator b = member_types->begin(),
                                               e = member_types->end();
                    b != e;
                    ++b) {
                base_types.insert((*b)->base_type);
            }
            if ((member_types->size() <= 4)
                    && (base_types.size() == 1)
                    && ((base_types.find(BaseType::Double) != base_types.end()) ||
                        (base_types.find(BaseType::Float)  != base_types.end()))) {
                type_enum = 1;
            }
        }
        if ((type->base_type == BaseType::Double) ||
                (type->base_type == BaseType::Float)) {
            type_enum = 2;
        }

        /* Determine the alignment and size of the type. */
        ParseResult alignment_pr;
        bool res1 = Operation::Alignmentof(ctx, block, type, &alignment_pr);
        if (!res1) {
            return false;
        }
        int sizeof_type = Operation::SizeofGet(units->top(), type);
        if (!sizeof_type) {
            return false;
        }

        /* If the type enum is 1, then pass in the retval as the buffer,
         * plus the number of elements in the type. */
        llvm::IRBuilder<> builder(arglist_pr.block);
        llvm::Value *buffer = NULL;
        llvm::Value *elements = NULL;
        if (type_enum != 1) {
            buffer = getNullPointer(ctx->toLLVMType(
                                        ctx->tr->getPointerType(
                                            ctx->tr->type_void
                                        ), node, true));
            elements = ctx->nt->getLLVMZero();
        } else {
            if (pr->retval) {
                buffer = pr->retval;
                pr->retval_used = 1;
                pr->retval_type = ctx->tr->getPointerType(type);
            } else {
                buffer = builder.CreateAlloca(ctx->toLLVMType(type, node, true));
            }
            elements =
                ctx->nt->getNativeInt(
                    type->is_array ? type->array_size
                                : st->member_types.size()
                );
        }

        /* Call the helper function. */
        std::vector<llvm::Value *> call_args;
        call_args.push_back(
            builder.CreateBitCast(
                arglist_pr.getValue(ctx),
                ctx->toLLVMType(
                    ctx->tr->getPointerType(ctx->tr->type_void),
                    node, true
                )
            )
        );
        call_args.push_back(ctx->nt->getNativeInt(type_enum));
        call_args.push_back(alignment_pr.getValue(ctx));
        call_args.push_back(llvm::ConstantInt::get(ctx->nt->getNativeSizeType(), sizeof_type));
        call_args.push_back(buffer);
        call_args.push_back(elements);
        llvm::Value *ret =
            builder.CreateCall(va_arg_aarch64,
                                llvm::ArrayRef<llvm::Value *>(call_args));

        /* If the type enum is 1, nothing else needs to be done.
         * Otherwise, cast/copy the value for the return. */
        if (type_enum == 1) {
            if (pr->retval) {
                pr->set(arglist_pr.block, type, ret);
            } else {
                llvm::Value *bc =
                    builder.CreateBitCast(ret,
                                        ctx->toLLVMType(
                                            ctx->tr->getPointerType(type),
                                            node, true));
                llvm::Value *loaded =
                    builder.CreateLoad(bc);
                pr->set(arglist_pr.block, type, loaded);
            }
            return true;
        } else {
            llvm::Value *bc =
                builder.CreateBitCast(ret,
                                      ctx->toLLVMType(
                                        ctx->tr->getPointerType(type),
                                        node, true));
            llvm::Value *loaded =
                builder.CreateLoad(bc);
            if (pr->retval) {
                builder.CreateStore(loaded, pr->retval);
                pr->retval_used = 1;
                pr->retval_type = ctx->tr->getPointerType(type);
                pr->set(arglist_pr.block, type, ret);
            } else {
                pr->set(arglist_pr.block, type, loaded);
            }
            return true;
        }
    } else {
        fprintf(stderr, "Unsupported architecture for va-arg\n");
        abort();
    }
}
}
<|endoftext|>


<commit_before>/*
 * izonereport - produce json configuration zonereport
*/
#include "irods_client_api_table.hpp"
#include "irods_pack_table.hpp"
#include "rodsClient.hpp"
#include "parseCommandLine.hpp"
#include "rodsPath.hpp"
#include "lsUtil.hpp"
#include "irods_buffer_encryption.hpp"
#include "zone_report.hpp"
#include <string>
#include <iostream>

void usage() {
    const char *msgs[] = {
        "Usage: izonereport",
        "izonereport queries the entire iRODS Zone for configuration information.",
        "This configuration information be in the form of a json documentw which",
        "will validate against a particular version as found on http://schemas.irods.org.",
        " -h  this help",
        ""
    };
    int i;
    for ( i = 0;; i++ ) {
        if ( strlen( msgs[i] ) == 0 ) {
            break;
        }
        printf( "%s\n", msgs[i] );
    }
    printReleaseInfo( "izonereport" );
}

int
main( int _argc, char** ) {

    signal( SIGPIPE, SIG_IGN );

    if( _argc > 1 ) {
        usage();
        return 0;
    }

    rodsEnv myEnv;
    int status = getRodsEnv( &myEnv );
    if ( status < 0 ) {
        rodsLogError( LOG_ERROR, status, "main: getRodsEnv error. " );
        exit( 1 );
    }

    rErrMsg_t errMsg;
    rcComm_t *conn;
    conn = rcConnect(
               myEnv.rodsHost,
               myEnv.rodsPort,
               myEnv.rodsUserName,
               myEnv.rodsZone,
               0, &errMsg );

    if ( conn == NULL ) {
        exit( 2 );
    }

    // =-=-=-=-=-=-=-
    // initialize pluggable api table
    irods::pack_entry_table& pk_tbl = irods::get_pack_table();
    irods::api_entry_table& api_tbl = irods::get_client_api_table();
    init_api_table( api_tbl, pk_tbl );

    if ( strcmp( myEnv.rodsUserName, PUBLIC_USER_NAME ) != 0 ) {
        status = clientLogin( conn );
        if ( status != 0 ) {
            rcDisconnect( conn );
            exit( 7 );
        }
    }

    void *tmp_out = NULL;
    status = procApiRequest( conn, ZONE_REPORT_AN, NULL, NULL,
                             &tmp_out, NULL );
    if ( status < 0 ) {
        printf( "\n\nERROR - failed in call to rcZoneReport - %d\n", status );
        return 0;
    }
    else {
        bytesBuf_t* bbuf = static_cast< bytesBuf_t* >( tmp_out );

        // may not be properly null terminated
        std::string s( ( char* )bbuf->buf, bbuf->len );
        printf( "\n%s\n", s.c_str() );
    }

    rcDisconnect( conn );
}

<commit_msg>[#858] spelling and clarity<commit_after>/*
 * izonereport - produce json configuration zonereport
*/
#include "irods_client_api_table.hpp"
#include "irods_pack_table.hpp"
#include "rodsClient.hpp"
#include "parseCommandLine.hpp"
#include "rodsPath.hpp"
#include "lsUtil.hpp"
#include "irods_buffer_encryption.hpp"
#include "zone_report.hpp"
#include <string>
#include <iostream>

void usage() {
    const char *msgs[] = {
        "Usage: izonereport",
        "izonereport queries the entire iRODS Zone for configuration information.",
        "This configuration information will be generated in the form of a JSON",
        "document which will validate using schemas found at http://schemas.irods.org.",
        " -h  this help",
        ""
    };
    int i;
    for ( i = 0;; i++ ) {
        if ( strlen( msgs[i] ) == 0 ) {
            break;
        }
        printf( "%s\n", msgs[i] );
    }
    printReleaseInfo( "izonereport" );
}

int
main( int _argc, char** ) {

    signal( SIGPIPE, SIG_IGN );

    if( _argc > 1 ) {
        usage();
        return 0;
    }

    rodsEnv myEnv;
    int status = getRodsEnv( &myEnv );
    if ( status < 0 ) {
        rodsLogError( LOG_ERROR, status, "main: getRodsEnv error. " );
        exit( 1 );
    }

    rErrMsg_t errMsg;
    rcComm_t *conn;
    conn = rcConnect(
               myEnv.rodsHost,
               myEnv.rodsPort,
               myEnv.rodsUserName,
               myEnv.rodsZone,
               0, &errMsg );

    if ( conn == NULL ) {
        exit( 2 );
    }

    // =-=-=-=-=-=-=-
    // initialize pluggable api table
    irods::pack_entry_table& pk_tbl = irods::get_pack_table();
    irods::api_entry_table& api_tbl = irods::get_client_api_table();
    init_api_table( api_tbl, pk_tbl );

    if ( strcmp( myEnv.rodsUserName, PUBLIC_USER_NAME ) != 0 ) {
        status = clientLogin( conn );
        if ( status != 0 ) {
            rcDisconnect( conn );
            exit( 7 );
        }
    }

    void *tmp_out = NULL;
    status = procApiRequest( conn, ZONE_REPORT_AN, NULL, NULL,
                             &tmp_out, NULL );
    if ( status < 0 ) {
        printf( "\n\nERROR - failed in call to rcZoneReport - %d\n", status );
        return 0;
    }
    else {
        bytesBuf_t* bbuf = static_cast< bytesBuf_t* >( tmp_out );

        // may not be properly null terminated
        std::string s( ( char* )bbuf->buf, bbuf->len );
        printf( "\n%s\n", s.c_str() );
    }

    rcDisconnect( conn );
}

<|endoftext|>


<commit_before>#include "DukeMainWindow.hpp"

#include <duke/engine/overlay/StatisticsOverlay.hpp>
#include <duke/engine/overlay/OnScreenDisplayOverlay.hpp>
#include <duke/engine/overlay/AttributesOverlay.hpp>
#include <duke/engine/rendering/ImageRenderer.hpp>
#include <duke/time/Clock.hpp>
#include <duke/gl/GL.hpp>

#include <string>
#include <sstream>

namespace duke {

DukeMainWindow::DukeMainWindow(GLFWwindow *pWindow, const CmdLineParameters &parameters) :
		DukeGLFWWindow(pWindow), m_CmdLine(parameters), m_Player(parameters), m_GlyphRenderer(m_GeometryRenderer), m_Pan(m_Context.pan), m_Zoom(m_Context.zoom) {
	m_Context.pGlyphRenderer = &m_GlyphRenderer;
	m_Context.pGeometryRenderer = &m_GeometryRenderer;

	::glfwMakeContextCurrent(m_pWindow);
	::glfwGetWindowSize(m_pWindow, &m_WindowDim.x, &m_WindowDim.y);
	::glfwGetWindowPos(m_pWindow, &m_WindowPos.x, &m_WindowPos.y);
	::glfwSwapInterval(parameters.swapBufferInterval);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable(GL_DEPTH_TEST);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	using std::bind;
	using std::placeholders::_1;
	using std::placeholders::_2;

	mouseButtonCallback = bind(&DukeMainWindow::onMouseClick, this, _1, _2);
	mousePosCallback = bind(&DukeMainWindow::onMouseMove, this, _1, _2);
	scrollCallback = bind(&DukeMainWindow::onScroll, this, _1, _2);
	windowResizeCallback = bind(&DukeMainWindow::onWindowResize, this, _1, _2);
	charCallback = bind(&DukeMainWindow::onChar, this, _1);
	keyCallback = bind(&DukeMainWindow::onKey, this, _1, _2);
	registerCallbacks();

}

void DukeMainWindow::load(const Timeline& timeline, const FrameDuration& frameDuration, const FitMode fitMode, int speed) {
	m_Player.load(timeline, frameDuration);
	m_Player.setPlaybackSpeed(speed);
	m_Context.fitMode = fitMode;
}

void DukeMainWindow::onKey(int key, int action) {
	if (action == GLFW_PRESS || action == GLFW_REPEAT)
		m_KeyStrokes.push_back(key);
}

void DukeMainWindow::onChar(unsigned int unicodeCodePoint) {
	m_CharStrokes.push_back(unicodeCodePoint);
}

void DukeMainWindow::onWindowResize(int width, int height) {
	::glViewport(0, 0, width, height);
	m_WindowDim.x = width;
	m_WindowDim.y = height;
}

void DukeMainWindow::onMouseMove(int x, int y) {
	int dx = x - m_MousePos.x;
	int dy = y - m_MousePos.y;
	m_MousePos.x = x;
	m_MousePos.y = y;
	if (m_MouseLeftDown)
		onMouseDrag(dx, dy);
}

void DukeMainWindow::onMouseClick(int buttonId, int buttonState) {
	if (buttonId == GLFW_MOUSE_BUTTON_LEFT)
		m_MouseLeftDown = buttonState == GLFW_PRESS;
}

void DukeMainWindow::onScroll(double x, double y) {
	m_Zoom += y;
}

void DukeMainWindow::onMouseDrag(int dx, int dy) {
	m_Pan.x += dx;
	m_Pan.y -= dy;
}

namespace { // defining channel mask constants

using glm::bvec4;
static const auto r = bvec4(true, false, false, false);
static const auto g = bvec4(false, true, false, false);
static const auto b = bvec4(false, false, true, false);
static const auto a = bvec4(false, false, false, true);
static const auto all = bvec4(false);

}  // namespace

static bool setNextMode(FitMode &mode) {
	switch (mode) {
	case FitMode::FREE:
		mode = FitMode::INNER;
		return true;
	case FitMode::INNER:
		mode = FitMode::OUTER;
		return true;
	case FitMode::OUTER:
		mode = FitMode::ACTUAL;
		return false;
	case FitMode::ACTUAL:
		mode = FitMode::INNER;
		return true;
	}
	throw std::runtime_error("unknown fitmode");
}

static const char* getFitModeString(FitMode &mode) {
	switch (mode) {
	case FitMode::ACTUAL:
		return "Actual pixel";
	case FitMode::INNER:
		return "Fit inner frame";
	case FitMode::FREE:
		return "No fit";
	case FitMode::OUTER:
		return "Fit outer frame";
	}
	throw std::runtime_error("unknown fitmode");
}

void DukeMainWindow::run() {
	AttributesOverlay attributesOverlay(m_GlyphRenderer);
	OnScreenDisplayOverlay statusOverlay(m_GlyphRenderer);
	StatisticsOverlay statisticOverlay(m_GlyphRenderer, m_Player.getTimeline());
	bool showAttributesOverlay = false;
	bool showStatisticOverlay = true;

	SharedMesh pSquare = createSquare();

	size_t lastFrame = 0;
	auto milestone = duke_clock::now();
	bool running = true;

	const auto keyPressed = [=](int key) -> bool {
		return ::glfwGetKey(m_pWindow, key) == GLFW_PRESS;
	};

	const auto shouldClose = [=]() -> bool {
		return ::glfwWindowShouldClose(m_pWindow);
	};

	const auto togglePlayStop = [&]() -> bool {
		const int speed = m_Player.getPlaybackSpeed() == 0 ? 1 : 0;
		m_Player.setPlaybackSpeed(speed);
		return speed != 0;
	};

	const auto display = [&](const std::string &msg) {
		statusOverlay.setString(m_Context.liveTime, msg);
	};

	const auto displayExposure = [&]() {
		std::ostringstream oss;
		oss.precision(3);
		oss << "exposure "<< std::fixed << m_Context.exposure;
		display(oss.str());
	};

	while (running) {
		// fetching user inputs
		::glfwPollEvents();

		// checking pan position or zoom changed
		const bool notFreeMode = m_Context.fitMode != FitMode::FREE;
		const bool panChanged = m_Context.pan != m_Pan;
		const bool zoomChanged = m_Context.zoom != m_Zoom;
		const bool switchToFreeFitMode = notFreeMode && (panChanged || zoomChanged);
		if (switchToFreeFitMode) {
			m_Context.fitMode = FitMode::FREE;
			m_Pan = glm::ivec2();
			m_Zoom = 0;
		}

		// setting up context
		m_Context.viewport = Viewport(glm::ivec2(), m_WindowDim);
		m_Context.currentFrame = m_Player.getCurrentFrame();
		m_Context.playbackTime = m_Player.getPlaybackTime();
		m_Context.pan = m_Pan;
		m_Context.zoom = m_Zoom;

		// current frame
		const size_t frame = m_Context.currentFrame.round();

		// preparing current frame textures
		auto &textureCache = m_Player.getTextureCache();
		const auto speed = m_Player.getPlaybackSpeed();
		const auto mode = speed < 0 ? IterationMode::BACKWARD : (speed > 0 ? IterationMode::FORWARD : IterationMode::PINGPONG);
		textureCache.prepare(frame, mode);

		// rendering tracks
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		for (const Track &track : m_Player.getTimeline()) {
			if (track.disabled)
				continue;

			const auto pTrackItr = track.clipContaining(frame);
			if (pTrackItr == track.end())
				continue;

			m_Context.pCurrentImage = nullptr;
			const MediaFrameReference mfr = track.getMediaFrameReferenceAt(frame);
			const auto pMediaStream = mfr.first;
			if (pMediaStream) {
				auto pLoadedTexture = textureCache.getLoadedTexture(mfr);
				if (pLoadedTexture) {
					m_Context.pCurrentImage = pLoadedTexture;
					auto &texture = *pLoadedTexture->pTexture;
					auto boundTexture = texture.scope_bind_texture();
					glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
					glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
					renderWithBoundTexture(m_GlyphRenderer.getGeometryRenderer().shaderPool, pSquare.get(), m_Context);
				} else {
					drawText(m_GlyphRenderer, m_Context.viewport, "missing frame", 100, 100, 1, 3);
				}
			}
			const auto& pOverlayTrack = pTrackItr->second.pOverlay;
			if (pOverlayTrack)
				pOverlayTrack->render(m_Context);
			if (showAttributesOverlay)
				attributesOverlay.render(m_Context);
		}
		statusOverlay.render(m_Context);
		if (showStatisticOverlay)
			statisticOverlay.render(m_Context);

		// displaying
		::glfwSwapBuffers(m_pWindow);

		// updating time
		const auto elapsedMicroSeconds = statisticOverlay.vBlankMetronom.tick();
		const Time offset = m_CmdLine.unlimitedFPS ? m_Player.getFrameDuration() : Time(elapsedMicroSeconds);
		m_Player.offsetPlaybackTime(offset);
		m_Context.liveTime += Time(elapsedMicroSeconds.count(), 1000000);

		if (frame != lastFrame) {
			statisticOverlay.frameMetronom.tick();
			lastFrame = frame;
		}

		// handling input by char
		for (const int key : m_CharStrokes) {
			switch (key) {
			case ' ': {
				const bool playing = togglePlayStop();
				display(playing ? "play" : "stop");
				break;
			}
			case 'r':
				m_Context.channels = m_Context.channels == r ? all : r;
				break;
			case 'g':
				m_Context.channels = m_Context.channels == g ? all : g;
				break;
			case 'b':
				m_Context.channels = m_Context.channels == b ? all : b;
				break;
			case 'a':
				m_Context.channels = m_Context.channels == a ? all : a;
				break;
			case '*':
				m_Context.exposure = 1;
				displayExposure();
				break;
			case '+':
				m_Context.exposure *= 1.2;
				displayExposure();
				break;
			case '-':
				m_Context.exposure /= 1.2;
				displayExposure();
				break;
			case 'o':
				showAttributesOverlay = !showAttributesOverlay;
				break;
			case 's':
				showStatisticOverlay = !showStatisticOverlay;
				break;
			case 'f':
				setNextMode(m_Context.fitMode);
				m_Context.pan = m_Pan = glm::ivec2();
				m_Context.zoom = m_Zoom = 0;
				display(getFitModeString(m_Context.fitMode));
				break;
			}
		}
		m_CharStrokes.clear();

		// handling input by key
		const bool ctrlModifier = keyPressed(GLFW_KEY_LEFT_CONTROL) || keyPressed(GLFW_KEY_RIGHT_CONTROL);
		//		const bool shiftModifier = keyPressed(GLFW_KEY_LEFT_SHIFT) || keyPressed(GLFW_KEY_RIGHT_SHIFT);
		for (const int key : m_KeyStrokes) {
			switch (key) {
			case GLFW_KEY_HOME:
				m_Player.cue(m_Player.getTimeline().getRange().first);
				break;
			case GLFW_KEY_END:
				m_Player.cue(m_Player.getTimeline().getRange().last);
				break;
			case GLFW_KEY_LEFT:
				m_Player.cueRelative(ctrlModifier ? -25 : -1);
				break;
			case GLFW_KEY_RIGHT:
				m_Player.cueRelative(ctrlModifier ? 25 : 1);
				break;
			}
		}
		m_KeyStrokes.clear();

		// check stop
		running = !(shouldClose() || (keyPressed(GLFW_KEY_ESC)));

		// dumping cache state every 200 ms
		const auto now = duke_clock::now();
		if ((now - milestone) > std::chrono::milliseconds(100)) {
			textureCache.getImageCache().dumpState(statisticOverlay.cacheState);
			statisticOverlay.vBlankMetronom.compute();
			statisticOverlay.frameMetronom.compute();
			milestone = now;
		}
	}
}

} /* namespace duke */
<commit_msg>fixed #19 using 'm' key for metadata display instead of 'o'<commit_after>#include "DukeMainWindow.hpp"

#include <duke/engine/overlay/StatisticsOverlay.hpp>
#include <duke/engine/overlay/OnScreenDisplayOverlay.hpp>
#include <duke/engine/overlay/AttributesOverlay.hpp>
#include <duke/engine/rendering/ImageRenderer.hpp>
#include <duke/time/Clock.hpp>
#include <duke/gl/GL.hpp>

#include <string>
#include <sstream>

namespace duke {

DukeMainWindow::DukeMainWindow(GLFWwindow *pWindow, const CmdLineParameters &parameters) :
		DukeGLFWWindow(pWindow), m_CmdLine(parameters), m_Player(parameters), m_GlyphRenderer(m_GeometryRenderer), m_Pan(m_Context.pan), m_Zoom(m_Context.zoom) {
	m_Context.pGlyphRenderer = &m_GlyphRenderer;
	m_Context.pGeometryRenderer = &m_GeometryRenderer;

	::glfwMakeContextCurrent(m_pWindow);
	::glfwGetWindowSize(m_pWindow, &m_WindowDim.x, &m_WindowDim.y);
	::glfwGetWindowPos(m_pWindow, &m_WindowPos.x, &m_WindowPos.y);
	::glfwSwapInterval(parameters.swapBufferInterval);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable(GL_DEPTH_TEST);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	using std::bind;
	using std::placeholders::_1;
	using std::placeholders::_2;

	mouseButtonCallback = bind(&DukeMainWindow::onMouseClick, this, _1, _2);
	mousePosCallback = bind(&DukeMainWindow::onMouseMove, this, _1, _2);
	scrollCallback = bind(&DukeMainWindow::onScroll, this, _1, _2);
	windowResizeCallback = bind(&DukeMainWindow::onWindowResize, this, _1, _2);
	charCallback = bind(&DukeMainWindow::onChar, this, _1);
	keyCallback = bind(&DukeMainWindow::onKey, this, _1, _2);
	registerCallbacks();

}

void DukeMainWindow::load(const Timeline& timeline, const FrameDuration& frameDuration, const FitMode fitMode, int speed) {
	m_Player.load(timeline, frameDuration);
	m_Player.setPlaybackSpeed(speed);
	m_Context.fitMode = fitMode;
}

void DukeMainWindow::onKey(int key, int action) {
	if (action == GLFW_PRESS || action == GLFW_REPEAT)
		m_KeyStrokes.push_back(key);
}

void DukeMainWindow::onChar(unsigned int unicodeCodePoint) {
	m_CharStrokes.push_back(unicodeCodePoint);
}

void DukeMainWindow::onWindowResize(int width, int height) {
	::glViewport(0, 0, width, height);
	m_WindowDim.x = width;
	m_WindowDim.y = height;
}

void DukeMainWindow::onMouseMove(int x, int y) {
	int dx = x - m_MousePos.x;
	int dy = y - m_MousePos.y;
	m_MousePos.x = x;
	m_MousePos.y = y;
	if (m_MouseLeftDown)
		onMouseDrag(dx, dy);
}

void DukeMainWindow::onMouseClick(int buttonId, int buttonState) {
	if (buttonId == GLFW_MOUSE_BUTTON_LEFT)
		m_MouseLeftDown = buttonState == GLFW_PRESS;
}

void DukeMainWindow::onScroll(double x, double y) {
	m_Zoom += y;
}

void DukeMainWindow::onMouseDrag(int dx, int dy) {
	m_Pan.x += dx;
	m_Pan.y -= dy;
}

namespace { // defining channel mask constants

using glm::bvec4;
static const auto r = bvec4(true, false, false, false);
static const auto g = bvec4(false, true, false, false);
static const auto b = bvec4(false, false, true, false);
static const auto a = bvec4(false, false, false, true);
static const auto all = bvec4(false);

}  // namespace

static bool setNextMode(FitMode &mode) {
	switch (mode) {
	case FitMode::FREE:
		mode = FitMode::INNER;
		return true;
	case FitMode::INNER:
		mode = FitMode::OUTER;
		return true;
	case FitMode::OUTER:
		mode = FitMode::ACTUAL;
		return false;
	case FitMode::ACTUAL:
		mode = FitMode::INNER;
		return true;
	}
	throw std::runtime_error("unknown fitmode");
}

static const char* getFitModeString(FitMode &mode) {
	switch (mode) {
	case FitMode::ACTUAL:
		return "Actual pixel";
	case FitMode::INNER:
		return "Fit inner frame";
	case FitMode::FREE:
		return "No fit";
	case FitMode::OUTER:
		return "Fit outer frame";
	}
	throw std::runtime_error("unknown fitmode");
}

void DukeMainWindow::run() {
	AttributesOverlay metadataOverlay(m_GlyphRenderer);
	OnScreenDisplayOverlay statusOverlay(m_GlyphRenderer);
	StatisticsOverlay statisticOverlay(m_GlyphRenderer, m_Player.getTimeline());
	bool showMetadataOverlay = false;
	bool showStatisticOverlay = true;

	SharedMesh pSquare = createSquare();

	size_t lastFrame = 0;
	auto milestone = duke_clock::now();
	bool running = true;

	const auto keyPressed = [=](int key) -> bool {
		return ::glfwGetKey(m_pWindow, key) == GLFW_PRESS;
	};

	const auto shouldClose = [=]() -> bool {
		return ::glfwWindowShouldClose(m_pWindow);
	};

	const auto togglePlayStop = [&]() -> bool {
		const int speed = m_Player.getPlaybackSpeed() == 0 ? 1 : 0;
		m_Player.setPlaybackSpeed(speed);
		return speed != 0;
	};

	const auto display = [&](const std::string &msg) {
		statusOverlay.setString(m_Context.liveTime, msg);
	};

	const auto displayExposure = [&]() {
		std::ostringstream oss;
		oss.precision(3);
		oss << "exposure "<< std::fixed << m_Context.exposure;
		display(oss.str());
	};

	while (running) {
		// fetching user inputs
		::glfwPollEvents();

		// checking pan position or zoom changed
		const bool notFreeMode = m_Context.fitMode != FitMode::FREE;
		const bool panChanged = m_Context.pan != m_Pan;
		const bool zoomChanged = m_Context.zoom != m_Zoom;
		const bool switchToFreeFitMode = notFreeMode && (panChanged || zoomChanged);
		if (switchToFreeFitMode) {
			m_Context.fitMode = FitMode::FREE;
			m_Pan = glm::ivec2();
			m_Zoom = 0;
		}

		// setting up context
		m_Context.viewport = Viewport(glm::ivec2(), m_WindowDim);
		m_Context.currentFrame = m_Player.getCurrentFrame();
		m_Context.playbackTime = m_Player.getPlaybackTime();
		m_Context.pan = m_Pan;
		m_Context.zoom = m_Zoom;

		// current frame
		const size_t frame = m_Context.currentFrame.round();

		// preparing current frame textures
		auto &textureCache = m_Player.getTextureCache();
		const auto speed = m_Player.getPlaybackSpeed();
		const auto mode = speed < 0 ? IterationMode::BACKWARD : (speed > 0 ? IterationMode::FORWARD : IterationMode::PINGPONG);
		textureCache.prepare(frame, mode);

		// rendering tracks
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		for (const Track &track : m_Player.getTimeline()) {
			if (track.disabled)
				continue;

			const auto pTrackItr = track.clipContaining(frame);
			if (pTrackItr == track.end())
				continue;

			m_Context.pCurrentImage = nullptr;
			const MediaFrameReference mfr = track.getMediaFrameReferenceAt(frame);
			const auto pMediaStream = mfr.first;
			if (pMediaStream) {
				auto pLoadedTexture = textureCache.getLoadedTexture(mfr);
				if (pLoadedTexture) {
					m_Context.pCurrentImage = pLoadedTexture;
					auto &texture = *pLoadedTexture->pTexture;
					auto boundTexture = texture.scope_bind_texture();
					glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
					glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
					renderWithBoundTexture(m_GlyphRenderer.getGeometryRenderer().shaderPool, pSquare.get(), m_Context);
				} else {
					drawText(m_GlyphRenderer, m_Context.viewport, "missing frame", 100, 100, 1, 3);
				}
			}
			const auto& pOverlayTrack = pTrackItr->second.pOverlay;
			if (pOverlayTrack)
				pOverlayTrack->render(m_Context);
			if (showMetadataOverlay)
				metadataOverlay.render(m_Context);
		}
		statusOverlay.render(m_Context);
		if (showStatisticOverlay)
			statisticOverlay.render(m_Context);

		// displaying
		::glfwSwapBuffers(m_pWindow);

		// updating time
		const auto elapsedMicroSeconds = statisticOverlay.vBlankMetronom.tick();
		const Time offset = m_CmdLine.unlimitedFPS ? m_Player.getFrameDuration() : Time(elapsedMicroSeconds);
		m_Player.offsetPlaybackTime(offset);
		m_Context.liveTime += Time(elapsedMicroSeconds.count(), 1000000);

		if (frame != lastFrame) {
			statisticOverlay.frameMetronom.tick();
			lastFrame = frame;
		}

		// handling input by char
		for (const int key : m_CharStrokes) {
			switch (key) {
			case ' ': {
				const bool playing = togglePlayStop();
				display(playing ? "play" : "stop");
				break;
			}
			case 'r':
				m_Context.channels = m_Context.channels == r ? all : r;
				break;
			case 'g':
				m_Context.channels = m_Context.channels == g ? all : g;
				break;
			case 'b':
				m_Context.channels = m_Context.channels == b ? all : b;
				break;
			case 'a':
				m_Context.channels = m_Context.channels == a ? all : a;
				break;
			case '*':
				m_Context.exposure = 1;
				displayExposure();
				break;
			case '+':
				m_Context.exposure *= 1.2;
				displayExposure();
				break;
			case '-':
				m_Context.exposure /= 1.2;
				displayExposure();
				break;
			case 'm':
				showMetadataOverlay = !showMetadataOverlay;
				break;
			case 's':
				showStatisticOverlay = !showStatisticOverlay;
				break;
			case 'f':
				setNextMode(m_Context.fitMode);
				m_Context.pan = m_Pan = glm::ivec2();
				m_Context.zoom = m_Zoom = 0;
				display(getFitModeString(m_Context.fitMode));
				break;
			}
		}
		m_CharStrokes.clear();

		// handling input by key
		const bool ctrlModifier = keyPressed(GLFW_KEY_LEFT_CONTROL) || keyPressed(GLFW_KEY_RIGHT_CONTROL);
		//		const bool shiftModifier = keyPressed(GLFW_KEY_LEFT_SHIFT) || keyPressed(GLFW_KEY_RIGHT_SHIFT);
		for (const int key : m_KeyStrokes) {
			switch (key) {
			case GLFW_KEY_HOME:
				m_Player.cue(m_Player.getTimeline().getRange().first);
				break;
			case GLFW_KEY_END:
				m_Player.cue(m_Player.getTimeline().getRange().last);
				break;
			case GLFW_KEY_LEFT:
				m_Player.cueRelative(ctrlModifier ? -25 : -1);
				break;
			case GLFW_KEY_RIGHT:
				m_Player.cueRelative(ctrlModifier ? 25 : 1);
				break;
			}
		}
		m_KeyStrokes.clear();

		// check stop
		running = !(shouldClose() || (keyPressed(GLFW_KEY_ESC)));

		// dumping cache state every 200 ms
		const auto now = duke_clock::now();
		if ((now - milestone) > std::chrono::milliseconds(100)) {
			textureCache.getImageCache().dumpState(statisticOverlay.cacheState);
			statisticOverlay.vBlankMetronom.compute();
			statisticOverlay.frameMetronom.compute();
			milestone = now;
		}
	}
}

} /* namespace duke */
<|endoftext|>