<commit_before>// Copyright Paul Dardeau, SwampBits LLC 2014
// BSD License

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

#include <string>

#include "KernelEventServer.h"
#include "Socket.h"
#include "SocketServiceHandler.h"
#include "SocketRequest.h"
#include "MutexLock.h"
#include "Logger.h"
#include "ServerSocket.h"
#include "BasicException.h"
#include "ThreadingFactory.h"

using namespace std;
using namespace chaudiere;

//******************************************************************************

KernelEventServer::KernelEventServer(Mutex& fdMutex,
                                     Mutex& hwmConnectionsMutex,
                                     const std::string& serverName) :
   m_socketServiceHandler(NULL),
   m_serverPort(0),
   m_maxConnections(0),
   m_listenBacklog(10),
   m_listenerFD(-1),
   m_numberEventsReturned(0) {
}

//******************************************************************************

KernelEventServer::~KernelEventServer() {

   delete m_socketServiceHandler;

   if (-1 != m_listenerFD) {
      ::close(m_listenerFD);
   }
}

//******************************************************************************

bool KernelEventServer::init(SocketServiceHandler* socketServiceHandler,
                             int serverPort,
                             int maxConnections) {
   m_socketServiceHandler = socketServiceHandler;
   m_serverPort = serverPort;
   m_maxConnections = maxConnections;

   if (NULL == m_socketServiceHandler) {
      Logger::critical("no socket service handler set");
      return false;
   }

   if (m_serverPort <= 0) {
      Logger::critical("serverPort must be positive, non-zero value");
      return false;
   }

   if (m_maxConnections <= 0) {
      Logger::critical("maxConnections must be positive, non-zero value");
      return false;
   }

   ThreadingFactory* tf = ThreadingFactory::getThreadingFactory();
   m_busyFlagsMutex = tf->createMutex("busyFlags");

   m_listenerFD = Socket::createSocket();
   if (m_listenerFD == -1) {
      Logger::critical("error: unable to create server listening socket");
      return false;
   }

   if (!ServerSocket::setReuseAddr(m_listenerFD)) {
      Logger::critical("unable to set REUSEADDR for socket");
      return false;
   }

   if (!ServerSocket::bind(m_listenerFD, m_serverPort)) {
      Logger::critical("bind failed");
      return false;
   }

   if (!ServerSocket::listen(m_listenerFD, m_listenBacklog)) {
      Logger::critical("listen failed");
      return false;
   }

   return true;
}

//******************************************************************************

bool KernelEventServer::isValidDescriptor(int fd) const {
#ifdef __linux__
   return ::fcntl(fd, F_GETFD) != EBADF;
#else
   //TODO: put non-linux test of descriptor
   return true;
#endif
}

//******************************************************************************

void KernelEventServer::run() {
   struct sockaddr_in clientaddr;
   socklen_t addrlen = sizeof(clientaddr);
   int newfd;
   char msg[128];

   const std::string& handlerName = m_socketServiceHandler->getName();

   Logger::info(std::string("using handler: ") + handlerName);

   for (;;) {

      m_numberEventsReturned = getKernelEvents(m_maxConnections);

      if (m_numberEventsReturned < 1) {
         continue;
      }

      for (int index = 0; index < m_numberEventsReturned; ++index) {

         const int client_fd = fileDescriptorForEventIndex(index);

         if (client_fd == m_listenerFD) {
            newfd = ::accept(m_listenerFD, (struct sockaddr *)&clientaddr, &addrlen);
            if (newfd == -1) {
               Logger::warning("server accept failed");
            } else {
               if (!addFileDescriptorForRead(newfd)) {
                  Logger::critical("kernel event server failed adding read filter");
               }
            }
         } else {
            if (client_fd == 0) {
               continue;
            }

            if (!isValidDescriptor(client_fd)) {
               removeBusyFD(client_fd);
	       removeFileDescriptorFromRead(client_fd);
               continue;
            }

            if (isEventReadClose(index)) {
               removeBusyFD(client_fd);
               if (!removeFileDescriptorFromRead(client_fd)) {
                  Logger::warning("kernel event server failed to delete read filter");
               }
	       ::close(client_fd);
            } else if (isEventDisconnect(index)) {
               removeBusyFD(client_fd);
               if (!removeFileDescriptorFromRead(client_fd)) {
                  Logger::warning("kernel event server failed to delete read filter");
               }
	       ::close(client_fd);
            } else if (isEventRead(index)) {
	       if (removeFileDescriptorFromRead(client_fd)) {
                  // are we already busy with this socket?
                  const bool isAlreadyBusy = isBusyFD(client_fd);

                  if (!isAlreadyBusy) {
                     //if (!removeFileDescriptorFromRead(client_fd)) {
                     //   Logger::error("unable to remove file descriptor from read");
                     //}

                     setBusyFD(client_fd, true);

                     SocketRequest* socketRequest =
                        new SocketRequest(this, client_fd, NULL);
		     socketRequest->setSocketOwned(false);
                     socketRequest->setUserIndex(index);
                     socketRequest->setAutoDelete();

                     try {
                        m_socketServiceHandler->serviceSocket(socketRequest);
                     } catch (const BasicException& be) {
                        Logger::error("exception in serviceSocket on handler: " + be.whatString());
                     } catch (const std::exception& e) {
                        Logger::error("exception in serviceSocket on handler: " + std::string(e.what()));
                     } catch (...) {
                        Logger::error("exception in serviceSocket on handler");
                     }
                  } else {
                     //::snprintf(msg, 128, "already busy with socket %d", client_fd);
                     //Logger::warning(msg);
                  }
	       } else {
		  removeBusyFD(client_fd);
	       }

            }
         }
      }
   }  // for (;;)
}

//******************************************************************************

void KernelEventServer::notifySocketComplete(Socket* socket) {
   const int socketFD = socket->getFileDescriptor();
   if (socketFD == -1) {
      return;
   }

   // mark the fd as not being busy anymore
   setBusyFD(socketFD, false);

   if (!isValidDescriptor(socketFD)) {
      //if (!removeFileDescriptorFromRead(socketFD)) {
      //   Logger::error("unable to remove file descriptor from read");
      //}
      removeBusyFD(socketFD);
   } else {
      // add socket back to watch
      if (socket->isOpen()) {
         if (!addFileDescriptorForRead(socketFD)) {
            Logger::critical("kernel event add read filter failed");
         }
      } else {
	 removeBusyFD(socketFD);
      }
   }
}

//******************************************************************************

int KernelEventServer::getListenerSocketFileDescriptor() const {
   return m_listenerFD;
}

//******************************************************************************

bool KernelEventServer::isBusyFD(int fd) const {
   MutexLock locker(*m_busyFlagsMutex);
   unordered_map<int,bool>::const_iterator it = m_busyFlags.find(fd);
   if (it != m_busyFlags.end()) {
      return it->second;
   } else {
      return false;
   }
}

//******************************************************************************

void KernelEventServer::setBusyFD(int fd, bool busy) {
   MutexLock locker(*m_busyFlagsMutex);
   unordered_map<int,bool>::iterator it = m_busyFlags.find(fd);
   if (it != m_busyFlags.end()) {
      it->second = busy;
   } else {
      m_busyFlags[fd] = busy;
   }
}

//******************************************************************************

bool KernelEventServer::removeBusyFD(int fd) {
   MutexLock locker(*m_busyFlagsMutex);
   unordered_map<int,bool>::iterator it = m_busyFlags.find(fd);
   if (it != m_busyFlags.end()) {
      m_busyFlags.erase(it);
      return true;
   } else {
      return false;
   }
}

//******************************************************************************

<commit_msg>comment out unused variable<commit_after>// Copyright Paul Dardeau, SwampBits LLC 2014
// BSD License

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

#include <string>

#include "KernelEventServer.h"
#include "Socket.h"
#include "SocketServiceHandler.h"
#include "SocketRequest.h"
#include "MutexLock.h"
#include "Logger.h"
#include "ServerSocket.h"
#include "BasicException.h"
#include "ThreadingFactory.h"

using namespace std;
using namespace chaudiere;

//******************************************************************************

KernelEventServer::KernelEventServer(Mutex& fdMutex,
                                     Mutex& hwmConnectionsMutex,
                                     const std::string& serverName) :
   m_socketServiceHandler(NULL),
   m_serverPort(0),
   m_maxConnections(0),
   m_listenBacklog(10),
   m_listenerFD(-1),
   m_numberEventsReturned(0) {
}

//******************************************************************************

KernelEventServer::~KernelEventServer() {

   delete m_socketServiceHandler;

   if (-1 != m_listenerFD) {
      ::close(m_listenerFD);
   }
}

//******************************************************************************

bool KernelEventServer::init(SocketServiceHandler* socketServiceHandler,
                             int serverPort,
                             int maxConnections) {
   m_socketServiceHandler = socketServiceHandler;
   m_serverPort = serverPort;
   m_maxConnections = maxConnections;

   if (NULL == m_socketServiceHandler) {
      Logger::critical("no socket service handler set");
      return false;
   }

   if (m_serverPort <= 0) {
      Logger::critical("serverPort must be positive, non-zero value");
      return false;
   }

   if (m_maxConnections <= 0) {
      Logger::critical("maxConnections must be positive, non-zero value");
      return false;
   }

   ThreadingFactory* tf = ThreadingFactory::getThreadingFactory();
   m_busyFlagsMutex = tf->createMutex("busyFlags");

   m_listenerFD = Socket::createSocket();
   if (m_listenerFD == -1) {
      Logger::critical("error: unable to create server listening socket");
      return false;
   }

   if (!ServerSocket::setReuseAddr(m_listenerFD)) {
      Logger::critical("unable to set REUSEADDR for socket");
      return false;
   }

   if (!ServerSocket::bind(m_listenerFD, m_serverPort)) {
      Logger::critical("bind failed");
      return false;
   }

   if (!ServerSocket::listen(m_listenerFD, m_listenBacklog)) {
      Logger::critical("listen failed");
      return false;
   }

   return true;
}

//******************************************************************************

bool KernelEventServer::isValidDescriptor(int fd) const {
#ifdef __linux__
   return ::fcntl(fd, F_GETFD) != EBADF;
#else
   //TODO: put non-linux test of descriptor
   return true;
#endif
}

//******************************************************************************

void KernelEventServer::run() {
   struct sockaddr_in clientaddr;
   socklen_t addrlen = sizeof(clientaddr);
   int newfd;
   //char msg[128];

   const std::string& handlerName = m_socketServiceHandler->getName();

   Logger::info(std::string("using handler: ") + handlerName);

   for (;;) {

      m_numberEventsReturned = getKernelEvents(m_maxConnections);

      if (m_numberEventsReturned < 1) {
         continue;
      }

      for (int index = 0; index < m_numberEventsReturned; ++index) {

         const int client_fd = fileDescriptorForEventIndex(index);

         if (client_fd == m_listenerFD) {
            newfd = ::accept(m_listenerFD, (struct sockaddr *)&clientaddr, &addrlen);
            if (newfd == -1) {
               Logger::warning("server accept failed");
            } else {
               if (!addFileDescriptorForRead(newfd)) {
                  Logger::critical("kernel event server failed adding read filter");
               }
            }
         } else {
            if (client_fd == 0) {
               continue;
            }

            if (!isValidDescriptor(client_fd)) {
               removeBusyFD(client_fd);
	       removeFileDescriptorFromRead(client_fd);
               continue;
            }

            if (isEventReadClose(index)) {
               removeBusyFD(client_fd);
               if (!removeFileDescriptorFromRead(client_fd)) {
                  Logger::warning("kernel event server failed to delete read filter");
               }
	       ::close(client_fd);
            } else if (isEventDisconnect(index)) {
               removeBusyFD(client_fd);
               if (!removeFileDescriptorFromRead(client_fd)) {
                  Logger::warning("kernel event server failed to delete read filter");
               }
	       ::close(client_fd);
            } else if (isEventRead(index)) {
	       if (removeFileDescriptorFromRead(client_fd)) {
                  // are we already busy with this socket?
                  const bool isAlreadyBusy = isBusyFD(client_fd);

                  if (!isAlreadyBusy) {
                     //if (!removeFileDescriptorFromRead(client_fd)) {
                     //   Logger::error("unable to remove file descriptor from read");
                     //}

                     setBusyFD(client_fd, true);

                     SocketRequest* socketRequest =
                        new SocketRequest(this, client_fd, NULL);
		     socketRequest->setSocketOwned(false);
                     socketRequest->setUserIndex(index);
                     socketRequest->setAutoDelete();

                     try {
                        m_socketServiceHandler->serviceSocket(socketRequest);
                     } catch (const BasicException& be) {
                        Logger::error("exception in serviceSocket on handler: " + be.whatString());
                     } catch (const std::exception& e) {
                        Logger::error("exception in serviceSocket on handler: " + std::string(e.what()));
                     } catch (...) {
                        Logger::error("exception in serviceSocket on handler");
                     }
                  } else {
                     //::snprintf(msg, 128, "already busy with socket %d", client_fd);
                     //Logger::warning(msg);
                  }
	       } else {
		  removeBusyFD(client_fd);
	       }

            }
         }
      }
   }  // for (;;)
}

//******************************************************************************

void KernelEventServer::notifySocketComplete(Socket* socket) {
   const int socketFD = socket->getFileDescriptor();
   if (socketFD == -1) {
      return;
   }

   // mark the fd as not being busy anymore
   setBusyFD(socketFD, false);

   if (!isValidDescriptor(socketFD)) {
      //if (!removeFileDescriptorFromRead(socketFD)) {
      //   Logger::error("unable to remove file descriptor from read");
      //}
      removeBusyFD(socketFD);
   } else {
      // add socket back to watch
      if (socket->isOpen()) {
         if (!addFileDescriptorForRead(socketFD)) {
            Logger::critical("kernel event add read filter failed");
         }
      } else {
	 removeBusyFD(socketFD);
      }
   }
}

//******************************************************************************

int KernelEventServer::getListenerSocketFileDescriptor() const {
   return m_listenerFD;
}

//******************************************************************************

bool KernelEventServer::isBusyFD(int fd) const {
   MutexLock locker(*m_busyFlagsMutex);
   unordered_map<int,bool>::const_iterator it = m_busyFlags.find(fd);
   if (it != m_busyFlags.end()) {
      return it->second;
   } else {
      return false;
   }
}

//******************************************************************************

void KernelEventServer::setBusyFD(int fd, bool busy) {
   MutexLock locker(*m_busyFlagsMutex);
   unordered_map<int,bool>::iterator it = m_busyFlags.find(fd);
   if (it != m_busyFlags.end()) {
      it->second = busy;
   } else {
      m_busyFlags[fd] = busy;
   }
}

//******************************************************************************

bool KernelEventServer::removeBusyFD(int fd) {
   MutexLock locker(*m_busyFlagsMutex);
   unordered_map<int,bool>::iterator it = m_busyFlags.find(fd);
   if (it != m_busyFlags.end()) {
      m_busyFlags.erase(it);
      return true;
   } else {
      return false;
   }
}

//******************************************************************************

<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Axel Naumann <axel@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/UserInterface/UserInterface.h"

#include "cling/UserInterface/CompilationException.h"
#include "cling/Interpreter/RuntimeException.h"
#include "cling/Interpreter/StoredValueRef.h"
#include "cling/MetaProcessor/MetaProcessor.h"
#include "textinput/TextInput.h"
#include "textinput/StreamReader.h"
#include "textinput/TerminalDisplay.h"

#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include "llvm/Config/config.h"

// Fragment copied from LLVM's raw_ostream.cpp
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#endif

#if defined(LLVM_ON_WIN32)
#include <Shlobj.h>
#endif

#if defined(_MSC_VER)
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
# define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif
#endif

namespace {
  // Handle fatal llvm errors by throwing an exception.
  // Yes, throwing exceptions in error handlers is bad.
  // Doing nothing is pretty terrible, too.
  void exceptionErrorHandler(void * /*user_data*/,
                             const std::string& reason,
                             bool /*gen_crash_diag*/) {
    throw cling::CompilationException(reason);
  }
#if defined(LLVM_ON_UNIX)
  static void GetUserHomeDirectory(llvm::SmallVectorImpl<char>& str) {
    str.clear();
    const char* home = getenv("HOME");
    if (!home)
      home = "/";
    llvm::StringRef SRhome(home);
    str.insert(str.begin(), SRhome.begin(), SRhome.end());
  }
#elif defined(LLVM_ON_WIN32)
  static void GetUserHomeDirectory(llvm::SmallVectorImpl<char>& str) {
    str.reserve(MAX_PATH);
    HRESULT res = SHGetFolderPathA(NULL,
                                   CSIDL_FLAG_CREATE | CSIDL_APPDATA,
                                   NULL,
                                   SHGFP_TYPE_CURRENT,
                                   str.data());
    if (res != S_OK) {
      assert(0 && "Failed to get user home directory");
      llvm::StringRef SRhome("\\");
      str.insert(str.begin(), SRhome.begin(), SRhome.end());
    }
  }
#else
# error "Unsupported platform."
#endif
}

namespace cling {
  // Declared in CompilationException.h; vtable pinned here.
  CompilationException::~CompilationException() throw() {}

  UserInterface::UserInterface(Interpreter& interp) {
    // We need stream that doesn't close its file descriptor, thus we are not
    // using llvm::outs. Keeping file descriptor open we will be able to use
    // the results in pipes (Savannah #99234).
    static llvm::raw_fd_ostream m_MPOuts (STDOUT_FILENO, /*ShouldClose*/false);
    m_MetaProcessor.reset(new MetaProcessor(interp, m_MPOuts));
    llvm::install_fatal_error_handler(&exceptionErrorHandler);
  }

  UserInterface::~UserInterface() {}

  void UserInterface::runInteractively(bool nologo /* = false */) {
    if (!nologo) {
      PrintLogo();
    }

    // History file is $HOME/.cling_history
    static const char* histfile = ".cling_history";
    llvm::SmallString<512> histfilePath;
    GetUserHomeDirectory(histfilePath);
    llvm::sys::path::append(histfilePath, histfile);

    using namespace textinput;
    StreamReader* R = StreamReader::Create();
    TerminalDisplay* D = TerminalDisplay::Create();
    TextInput TI(*R, *D, histfilePath.c_str());

    TI.SetPrompt("[cling]$ ");
    std::string line;

    jmp_buf env;
    int val = setjmp(env);
    if (!val) {
      Interpreter::getNullDerefJump() = &env;
    } else {
      llvm::errs() << "LongJmp occurred. Recovering...\n";
    }
    while (true) {
      try {
        m_MetaProcessor->getOuts().flush();
        TextInput::EReadResult RR = TI.ReadInput();
        TI.TakeInput(line);
        if (RR == TextInput::kRREOF) {
          break;
        }

        cling::Interpreter::CompilationResult compRes;
        MetaProcessor::MaybeRedirectOutputRAII RAII(m_MetaProcessor.get());
        int indent
          = m_MetaProcessor->process(line.c_str(), compRes, 0/*result*/);
        // Quit requested
        if (indent < 0)
          break;
        std::string Prompt = "[cling]";
        if (m_MetaProcessor->getInterpreter().isRawInputEnabled())
          Prompt.append("! ");
        else
          Prompt.append("$ ");

        if (indent > 0)
          // Continuation requested.
          Prompt.append('?' + std::string(indent * 3, ' '));

        TI.SetPrompt(Prompt.c_str());

      }
      catch(runtime::NullDerefException& e) {
        e.diagnose();
      }
      catch(runtime::InterpreterException& e) {
        llvm::errs() << ">>> Caught an interpreter exception!\n"
                     << ">>> " << e.what() << '\n';
      }
      catch(std::exception& e) {
        llvm::errs() << ">>> Caught a std::exception!\n"
                     << ">>> " << e.what() << '\n';
      }
      catch(...) {
        llvm::errs() << "Exception occurred. Recovering...\n";
      }
    }
  }

  void UserInterface::PrintLogo() {
    llvm::raw_ostream& outs = m_MetaProcessor->getOuts();
    outs << "\n";
    outs << "****************** CLING ******************" << "\n";
    outs << "* Type C++ code and press enter to run it *" << "\n";
    outs << "*             Type .q to exit             *" << "\n";
    outs << "*******************************************" << "\n";
  }
} // end namespace cling
<commit_msg>Disable history if envvar CLING_NOHISTORY is set - for fixed memory address.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Axel Naumann <axel@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/UserInterface/UserInterface.h"

#include "cling/UserInterface/CompilationException.h"
#include "cling/Interpreter/RuntimeException.h"
#include "cling/Interpreter/StoredValueRef.h"
#include "cling/MetaProcessor/MetaProcessor.h"
#include "textinput/TextInput.h"
#include "textinput/StreamReader.h"
#include "textinput/TerminalDisplay.h"

#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include "llvm/Config/config.h"

// Fragment copied from LLVM's raw_ostream.cpp
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#endif

#if defined(LLVM_ON_WIN32)
#include <Shlobj.h>
#endif

#if defined(_MSC_VER)
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
# define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif
#endif

namespace {
  // Handle fatal llvm errors by throwing an exception.
  // Yes, throwing exceptions in error handlers is bad.
  // Doing nothing is pretty terrible, too.
  void exceptionErrorHandler(void * /*user_data*/,
                             const std::string& reason,
                             bool /*gen_crash_diag*/) {
    throw cling::CompilationException(reason);
  }
#if defined(LLVM_ON_UNIX)
  static void GetUserHomeDirectory(llvm::SmallVectorImpl<char>& str) {
    str.clear();
    const char* home = getenv("HOME");
    if (!home)
      home = "/";
    llvm::StringRef SRhome(home);
    str.insert(str.begin(), SRhome.begin(), SRhome.end());
  }
#elif defined(LLVM_ON_WIN32)
  static void GetUserHomeDirectory(llvm::SmallVectorImpl<char>& str) {
    str.reserve(MAX_PATH);
    HRESULT res = SHGetFolderPathA(NULL,
                                   CSIDL_FLAG_CREATE | CSIDL_APPDATA,
                                   NULL,
                                   SHGFP_TYPE_CURRENT,
                                   str.data());
    if (res != S_OK) {
      assert(0 && "Failed to get user home directory");
      llvm::StringRef SRhome("\\");
      str.insert(str.begin(), SRhome.begin(), SRhome.end());
    }
  }
#else
# error "Unsupported platform."
#endif
}

namespace cling {
  // Declared in CompilationException.h; vtable pinned here.
  CompilationException::~CompilationException() throw() {}

  UserInterface::UserInterface(Interpreter& interp) {
    // We need stream that doesn't close its file descriptor, thus we are not
    // using llvm::outs. Keeping file descriptor open we will be able to use
    // the results in pipes (Savannah #99234).
    static llvm::raw_fd_ostream m_MPOuts (STDOUT_FILENO, /*ShouldClose*/false);
    m_MetaProcessor.reset(new MetaProcessor(interp, m_MPOuts));
    llvm::install_fatal_error_handler(&exceptionErrorHandler);
  }

  UserInterface::~UserInterface() {}

  void UserInterface::runInteractively(bool nologo /* = false */) {
    if (!nologo) {
      PrintLogo();
    }

    llvm::SmallString<512> histfilePath;
    if (!getenv("CLING_NOHISTORY")) {
      // History file is $HOME/.cling_history
      static const char* histfile = ".cling_history";
      GetUserHomeDirectory(histfilePath);
      llvm::sys::path::append(histfilePath, histfile);
    }

    using namespace textinput;
    StreamReader* R = StreamReader::Create();
    TerminalDisplay* D = TerminalDisplay::Create();
    TextInput TI(*R, *D, histfilePath.empty() ? 0 : histfilePath.c_str());

    TI.SetPrompt("[cling]$ ");
    std::string line;

    jmp_buf env;
    int val = setjmp(env);
    if (!val) {
      Interpreter::getNullDerefJump() = &env;
    } else {
      llvm::errs() << "LongJmp occurred. Recovering...\n";
    }
    while (true) {
      try {
        m_MetaProcessor->getOuts().flush();
        TextInput::EReadResult RR = TI.ReadInput();
        TI.TakeInput(line);
        if (RR == TextInput::kRREOF) {
          break;
        }

        cling::Interpreter::CompilationResult compRes;
        MetaProcessor::MaybeRedirectOutputRAII RAII(m_MetaProcessor.get());
        int indent
          = m_MetaProcessor->process(line.c_str(), compRes, 0/*result*/);
        // Quit requested
        if (indent < 0)
          break;
        std::string Prompt = "[cling]";
        if (m_MetaProcessor->getInterpreter().isRawInputEnabled())
          Prompt.append("! ");
        else
          Prompt.append("$ ");

        if (indent > 0)
          // Continuation requested.
          Prompt.append('?' + std::string(indent * 3, ' '));

        TI.SetPrompt(Prompt.c_str());

      }
      catch(runtime::NullDerefException& e) {
        e.diagnose();
      }
      catch(runtime::InterpreterException& e) {
        llvm::errs() << ">>> Caught an interpreter exception!\n"
                     << ">>> " << e.what() << '\n';
      }
      catch(std::exception& e) {
        llvm::errs() << ">>> Caught a std::exception!\n"
                     << ">>> " << e.what() << '\n';
      }
      catch(...) {
        llvm::errs() << "Exception occurred. Recovering...\n";
      }
    }
  }

  void UserInterface::PrintLogo() {
    llvm::raw_ostream& outs = m_MetaProcessor->getOuts();
    outs << "\n";
    outs << "****************** CLING ******************" << "\n";
    outs << "* Type C++ code and press enter to run it *" << "\n";
    outs << "*             Type .q to exit             *" << "\n";
    outs << "*******************************************" << "\n";
  }
} // end namespace cling
<|endoftext|>


<commit_before>#ifndef TEMPLATE_RATIONAL_HPP_
#define TEMPLATE_RATIONAL_HPP_

#include<iostream>

template<int a>
struct equal_zero{
	static const bool value = false;
};

template<>
struct equal_zero<0>{
	static const bool value = true;
};

template<int a>
struct abs_t{
	static const unsigned value = ( a < 0 ) ? -a : a;
};

template<int a, int b>
struct calc_gcd{
	static const int value = calc_gcd<b, a%b>::value;
};

template<int a>
struct calc_gcd<a,0> {
	static const int value = abs_t<a>::value;
};

// container for numerator and denominator
template<int num, unsigned den>
struct Rat {
	static const int v_num = num;
	static const unsigned v_den = den;
};

//catch division 0
template<int num>
struct Rat<num, 0>;

template<int num, unsigned den>
struct Rational_min {
	static const int gcd = calc_gcd<num,den>::value;
	using value = Rat<num / gcd, den / gcd>;
};

// zero is representet by 0/1
template<int den>
struct Rational_min<0, den> {
	using value = Rat<0,1>;
};

// defining Rational
template<int num, unsigned den>
using Rational = typename Rational_min<num,den>::value;

// Raw addition of rational numbers
template<int num1, unsigned den1, int num2, unsigned den2>
struct R_add_meta{
	static const unsigned den = den1 * den2;
	using value = typename R_add_meta<num1 * den2, den, num2 * den1, den>::value;
};

// addition for same denominator
template<int num1, int num2, unsigned den>
struct R_add_meta<num1, den, num2, den>{
	using value = Rational<num1+num2, den>;
};

template<class R1, class R2>
using R_add = typename R_add_meta<R1::v_num, R1::v_den, R2::v_num, R2::v_den>::value;

template<class R>
using R_neg = Rat<-R::v_num, R::v_den>;

template<class R1, class R2>
using R_sub = R_add<R1, R_neg<R2>>;

template<class R1, class R2>
using R_mul = Rational<R1::v_num * R2::v_num, R1::v_den * R2::v_den>;

template<int num, unsigned den>
struct R_inv_meta{
	using value = Rat< (num < 0) ? -den : den, abs_t<num>::value>;
};

template<unsigned den>
struct R_inv_meta<0,den>{
	using value = Rat<0,1>;
};

template<class R>
using R_inv = typename R_inv_meta<R::v_num, R::v_den>::value;

template<class R1, class R2>
using R_div = R_mul<R1, R_inv<R2>>;

template<int num, unsigned den>
struct R_print_raw{
	static constexpr std::ostream& print(std::ostream& os = std::cout) {
		return os << num << '/' << den;
	}
};

template<int num>
struct R_print_raw<num,1>{
	static constexpr std::ostream& print(std::ostream& os) {
		return os << num;
	}
};

template<class R>
struct R_print{
	static constexpr std::ostream& print(std::ostream& os = std::cout) {
		return R_print_raw<R::v_num, R::v_den>::print(os);
	}
};

#endif //TEMPLATE_RATIONAL_HPP_
<commit_msg>Rational is_one hinzugefügt<commit_after>#ifndef TEMPLATE_RATIONAL_HPP_
#define TEMPLATE_RATIONAL_HPP_

#include<iostream>

template<int a>
struct is_zero{
	static const bool value = false;
};

template<>
struct is_zero<0>{
	static const bool value = true;
};

template<int a>
struct is_one{
	static const bool value = false;
};

template<>
struct is_one<1>{
	static const bool value = true;
};

template<int a>
struct abs_t{
	static const unsigned value = ( a < 0 ) ? -a : a;
};

template<int a, int b>
struct calc_gcd{
	static const int value = calc_gcd<b, a%b>::value;
};

template<int a>
struct calc_gcd<a,0> {
	static const int value = abs_t<a>::value;
};

// container for numerator and denominator
template<int num, unsigned den>
struct Rat {
	static const int v_num = num;
	static const unsigned v_den = den;
};

//catch division 0
template<int num>
struct Rat<num, 0>;

template<int num, unsigned den>
struct Rational_min {
	static const int gcd = calc_gcd<num,den>::value;
	using value = Rat<num / gcd, den / gcd>;
};

// zero is representet by 0/1
template<int den>
struct Rational_min<0, den> {
	using value = Rat<0,1>;
};

// defining Rational
template<int num, unsigned den>
using Rational = typename Rational_min<num,den>::value;

template<class R>
using R_is_zero = is_zero<R::v_num>;

template<class R>
using R_is_one = is_one<R::v_num>;

// Raw addition of rational numbers
template<int num1, unsigned den1, int num2, unsigned den2>
struct R_add_meta{
	static const unsigned den = den1 * den2;
	using value = typename R_add_meta<num1 * den2, den, num2 * den1, den>::value;
};

// addition for same denominator
template<int num1, int num2, unsigned den>
struct R_add_meta<num1, den, num2, den>{
	using value = Rational<num1+num2, den>;
};

template<class R1, class R2>
using R_add = typename R_add_meta<R1::v_num, R1::v_den, R2::v_num, R2::v_den>::value;

template<class R>
using R_neg = Rat<-R::v_num, R::v_den>;

template<class R1, class R2>
using R_sub = R_add<R1, R_neg<R2>>;

template<class R1, class R2>
using R_mul = Rational<R1::v_num * R2::v_num, R1::v_den * R2::v_den>;

template<int num, unsigned den>
struct R_inv_meta{
	using value = Rat< (num < 0) ? -den : den, abs_t<num>::value>;
};

template<unsigned den>
struct R_inv_meta<0,den>{
	using value = Rat<0,1>;
};

template<class R>
using R_inv = typename R_inv_meta<R::v_num, R::v_den>::value;

template<class R1, class R2>
using R_div = R_mul<R1, R_inv<R2>>;

template<int num, unsigned den>
struct R_print_raw{
	static constexpr std::ostream& print(std::ostream& os = std::cout) {
		return os << num << '/' << den;
	}
};

template<int num>
struct R_print_raw<num,1>{
	static constexpr std::ostream& print(std::ostream& os) {
		return os << num;
	}
};

template<class R>
struct R_print{
	static constexpr std::ostream& print(std::ostream& os = std::cout) {
		return R_print_raw<R::v_num, R::v_den>::print(os);
	}
};

#endif //TEMPLATE_RATIONAL_HPP_
<|endoftext|>


<commit_before>#include "Stdafx.h"
#include "SceneTextRegionExtractor.h"

using namespace std;
using namespace cv;

namespace ImageQuality
{
	IList<array<byte>^>^ SceneTextRegionExtractor::SimpleWatermark(array<byte>^ buffer)
	{
		Mat img = ReadImage(buffer);

		List<array<byte>^>^ images = gcnew List<array<byte>^>(4);

		Mat red = DetectAndRotate(img, Scalar(0, 0, 200), Scalar(50, 50, 255));
		images->Add(ToByteArray(red, ".tiff"));

		Mat yellow = DetectAndRotate(img, Scalar(0, 225, 225), Scalar(50, 255, 255));
		images->Add(ToByteArray(yellow, ".tiff"));

		Mat black = DetectAndRotate(img, Scalar(0, 0, 0), Scalar(25, 25, 25));
		images->Add(ToByteArray(black, ".tiff"));

		Mat white = DetectAndRotate(img, Scalar(200, 200, 200), Scalar(255, 255, 255));
		images->Add(ToByteArray(white, ".tiff"));

		return images;
	}

	Mat SceneTextRegionExtractor::DetectAndRotate(Mat img, Scalar lower, Scalar upper)
	{
		Mat output;
		inRange(img, lower, upper, output);

		Mat connected;
		pyrDown(output, connected);
		Mat morphKernel = getStructuringElement(MORPH_RECT, Size(5, 1));
		morphologyEx(connected, connected, MORPH_CLOSE, morphKernel);

		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		if (hierarchy.empty())
			return output;

		float biggest = 0, angle;
		for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
		{
			RotatedRect box = minAreaRect(contours[idx]);

			float area = box.size.area();
			if (area > biggest && box.size.width > 20 && box.size.height > 12)
			{
				biggest = area;
				angle = box.angle;
			}
		}

		if (angle < -45)
			angle += 90;

		int len = max(output.cols, output.rows);
		Point2f pt(len / 2., len / 2.);
		Mat r = getRotationMatrix2D(pt, angle, 1);
		warpAffine(output, output, r, Size(len, len));

		return output;
	}

	IList<Region^>^ SceneTextRegionExtractor::GetRegions(array<byte>^ buffer, Stream^ ocrImgStream, Stream^ regionStream)
	{
		Mat image = ReadImage(buffer);

		if (ocrImgStream != nullptr)
		{
			Mat tiff;
			cvtColor(image, tiff, CV_BGR2GRAY);
			threshold(tiff, tiff, 0, 255, THRESH_BINARY | THRESH_OTSU);
			WriteToStream(".tiff", tiff, ocrImgStream);
		}

		Mat rgb;
		pyrDown(image, rgb);
		Mat small;
		cvtColor(rgb, small, CV_BGR2GRAY);

		Mat grad;
		Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
		morphologyEx(small, grad, MORPH_GRADIENT, morphKernel);

		Mat bw;
		threshold(grad, bw, 0, 255, THRESH_BINARY | THRESH_OTSU);

		Mat connected;
		morphKernel = getStructuringElement(MORPH_RECT, Size(5, 1));
		morphologyEx(bw, connected, MORPH_CLOSE, morphKernel);

		Mat mask = Mat::zeros(bw.size(), CV_8UC1);
		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		List<Region^>^ list = gcnew List<Region^>(5);
		for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
		{
			Rect rect = boundingRect(contours[idx]);
			Mat maskROI(mask, rect);
			maskROI = Scalar(0, 0, 0);

			drawContours(mask, contours, idx, Scalar(255, 255, 255), CV_FILLED);
			double r = (double)countNonZero(maskROI) / (rect.width*rect.height);

			if (r > .45 && rect.width > 20 && rect.height > 12)
			{
				rectangle(rgb, rect, Scalar(0, 255, 0), 2);

				Region^ region = gcnew Region(rect.x * 2, rect.y * 2, rect.width * 2, rect.height * 2);
				list->Add(region);
			}
		}

		if (regionStream != nullptr)
		{
			WriteToStream(".jpg", rgb, regionStream);
		}

		return list;
	}

	Mat SceneTextRegionExtractor::ReadImage(array<byte>^ buffer)
	{
		pin_ptr<byte> px = &buffer[0];
		Mat datax(1, buffer->Length, CV_8U, (void*)px, CV_AUTO_STEP);
		return imdecode(datax, CV_LOAD_IMAGE_COLOR);
	}

	array<byte>^ SceneTextRegionExtractor::ToByteArray(Mat image, const std::string& extension)
	{
		vector<byte> buffer;
		imencode(extension, image, buffer);

		array<byte>^ bytes = gcnew array<byte>(buffer.size());

		UnmanagedMemoryStream^ sourceStream = gcnew UnmanagedMemoryStream(buffer.data(), buffer.size());
		sourceStream->Read(bytes, 0, buffer.size());

		return bytes;
	}

	void SceneTextRegionExtractor::WriteToStream(const std::string& extension, Mat image, Stream^ stream)
	{
		vector<byte> buffer;
		imencode(extension, image, buffer);

		UnmanagedMemoryStream^ sourceStream = gcnew UnmanagedMemoryStream(buffer.data(), buffer.size());
		sourceStream->CopyTo(stream);
	}
}<commit_msg>Text detection tweaks<commit_after>#include "Stdafx.h"
#include "SceneTextRegionExtractor.h"

using namespace std;
using namespace cv;

namespace ImageQuality
{
	IList<array<byte>^>^ SceneTextRegionExtractor::SimpleWatermark(array<byte>^ buffer)
	{
		Mat img = ReadImage(buffer);

		List<array<byte>^>^ images = gcnew List<array<byte>^>(4);

		Mat red = DetectAndRotate(img, Scalar(0, 0, 200), Scalar(50, 50, 255));
		images->Add(ToByteArray(red, ".tiff"));

		Mat yellow = DetectAndRotate(img, Scalar(0, 225, 225), Scalar(50, 255, 255));
		images->Add(ToByteArray(yellow, ".tiff"));

		Mat black = DetectAndRotate(img, Scalar(0, 0, 0), Scalar(25, 25, 25));
		images->Add(ToByteArray(black, ".tiff"));

		Mat white = DetectAndRotate(img, Scalar(200, 200, 200), Scalar(255, 255, 255));
		images->Add(ToByteArray(white, ".tiff"));

		return images;
	}

	Mat SceneTextRegionExtractor::DetectAndRotate(Mat img, Scalar lower, Scalar upper)
	{
		Mat output;
		inRange(img, lower, upper, output);

		Mat connected;
		pyrDown(output, connected);
		Mat morphKernel = getStructuringElement(MORPH_RECT, Size(5, 1));
		morphologyEx(connected, connected, MORPH_CLOSE, morphKernel);

		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		if (hierarchy.empty())
			return output;

		float biggest = 0, angle;
		for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
		{
			RotatedRect box = minAreaRect(contours[idx]);

			float area = box.size.area();
			if (area > biggest && box.size.width > 20 && box.size.height > 12)
			{
				biggest = area;
				angle = box.angle;
			}
		}

		if (angle < -45)
			angle += 90;

		int len = max(output.cols, output.rows);
		Point2f pt(len / 2., len / 2.);
		Mat r = getRotationMatrix2D(pt, angle, 1);
		warpAffine(output, output, r, Size(len, len));

		return output;
	}

	IList<Region^>^ SceneTextRegionExtractor::GetRegions(array<byte>^ buffer, Stream^ ocrImgStream, Stream^ regionStream)
	{
		Mat image = ReadImage(buffer);

		if (ocrImgStream != nullptr)
		{
			Mat tiff;
			cvtColor(image, tiff, CV_BGR2GRAY);
			threshold(tiff, tiff, 0, 255, THRESH_BINARY | THRESH_OTSU);
			WriteToStream(".tiff", tiff, ocrImgStream);
		}

		Mat gray;
		cvtColor(image, gray, CV_BGR2GRAY);

		Mat scaled = gray;
		pyrDown(scaled, scaled);
		pyrUp(scaled, scaled);

		Mat grad;
		Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
		morphologyEx(scaled, grad, MORPH_GRADIENT, morphKernel);

		Mat bw;
		threshold(grad, bw, 32, 255, THRESH_BINARY | THRESH_OTSU);

		Mat connected;
		morphKernel = getStructuringElement(MORPH_RECT, Size(5, 1));
		morphologyEx(bw, connected, MORPH_CLOSE, morphKernel);

		Mat mask = Mat::zeros(bw.size(), CV_8UC1);
		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		vector<Rect> regionRects;
		for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
		{
			Rect rect = boundingRect(contours[idx]);
			if (rect.height > 400)
				continue;

			Mat maskROI(mask, rect);
			maskROI = Scalar(0, 0, 0);

			drawContours(mask, contours, idx, Scalar(255, 255, 255), CV_FILLED);
			double r = (double)countNonZero(maskROI) / (rect.width*rect.height);

			if (r > .45 && rect.width > 20 && rect.height > 12)
			{
				regionRects.push_back(rect);
			}
		}

		List<Region^>^ list = gcnew List<Region^>(5);
		for each (Rect rect in regionRects)
		{
			rectangle(image, rect, Scalar(0, 255, 0), 2);

			Region^ region = gcnew Region(rect.x, rect.y, rect.width, rect.height);
			list->Add(region);
		}

		if (regionStream != nullptr)
		{
			WriteToStream(".jpg", image, regionStream);
		}

		return list;
	}

	Mat SceneTextRegionExtractor::ReadImage(array<byte>^ buffer)
	{
		pin_ptr<byte> px = &buffer[0];
		Mat datax(1, buffer->Length, CV_8U, (void*)px, CV_AUTO_STEP);
		return imdecode(datax, CV_LOAD_IMAGE_COLOR);
	}

	array<byte>^ SceneTextRegionExtractor::ToByteArray(Mat image, const std::string& extension)
	{
		vector<byte> buffer;
		imencode(extension, image, buffer);

		array<byte>^ bytes = gcnew array<byte>(buffer.size());

		UnmanagedMemoryStream^ sourceStream = gcnew UnmanagedMemoryStream(buffer.data(), buffer.size());
		sourceStream->Read(bytes, 0, buffer.size());

		return bytes;
	}

	void SceneTextRegionExtractor::WriteToStream(const std::string& extension, Mat image, Stream^ stream)
	{
		vector<byte> buffer;
		imencode(extension, image, buffer);

		UnmanagedMemoryStream^ sourceStream = gcnew UnmanagedMemoryStream(buffer.data(), buffer.size());
		sourceStream->CopyTo(stream);
	}
}<|endoftext|>


<commit_before>#include <QObject>
#include <QTest>
#include <QDebug>

#include "Generator.hpp"

const QString c_typesSection = QStringLiteral("---types---");
const QString c_functionsSection = QStringLiteral("---functions---");

static QByteArray generateTextSpec(const QStringList &types, const QStringList &functions = {})
{
    QByteArray result;
    if (!types.isEmpty()) {
        const QString textData = c_typesSection + QLatin1Char('\n') + types.join(QLatin1Char('\n')) + QLatin1Char('\n');
        result += textData.toLocal8Bit();
    }
    if (!functions.isEmpty()) {
        const QString textData = c_functionsSection + QLatin1Char('\n') + functions.join(QLatin1Char('\n')) + QLatin1Char('\n');
        result += textData.toLocal8Bit();
    }
    return result;
}

const QStringList c_sourcesInputMediaDeps =
{
    QStringLiteral("inputPhotoEmpty = InputPhoto;"),
    QStringLiteral("inputFileEmpty = InputFile;"),
    QStringLiteral("inputVideoEmpty = InputVideo;"),
    QStringLiteral("inputAudioEmpty = InputAudio;"),
    QStringLiteral("inputDocumentEmpty = InputDocument;"),
    QStringLiteral("documentAttributeEmpty = DocumentAttribute;"),
};

const QStringList c_sourcesInputMedia =
{
    QStringLiteral("inputMediaEmpty#9664f57f = InputMedia;"),
    QStringLiteral("inputMediaUploadedPhoto#f7aff1c0 file:InputFile caption:string = InputMedia;"),
    QStringLiteral("inputMediaPhoto#e9bfb4f3 id:InputPhoto caption:string = InputMedia;"),
    QStringLiteral("inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;"),
    QStringLiteral("inputMediaUploadedVideo#82713fdf file:InputFile duration:int w:int h:int mime_type:string caption:string = InputMedia;"),
    QStringLiteral("inputMediaUploadedThumbVideo#7780ddf9 file:InputFile thumb:InputFile duration:int w:int h:int mime_type:string caption:string = InputMedia;"),
    QStringLiteral("inputMediaVideo#936a4ebd id:InputVideo caption:string = InputMedia;"),
    QStringLiteral("inputMediaUploadedAudio#4e498cab file:InputFile duration:int mime_type:string = InputMedia;"),
    QStringLiteral("inputMediaAudio#89938781 id:InputAudio = InputMedia;"),
    QStringLiteral("inputMediaUploadedDocument#1d89306d file:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string = InputMedia;"),
    QStringLiteral("inputMediaUploadedThumbDocument#ad613491 file:InputFile thumb:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string = InputMedia;"),
    QStringLiteral("inputMediaDocument#1a77f29c id:InputDocument caption:string = InputMedia;"),
};

const QStringList c_sourcesRichText =
{
    QStringLiteral("textEmpty#dc3d824f = RichText;"),
    QStringLiteral("textPlain#744694e0 text:string = RichText;"),
    QStringLiteral("textBold#6724abc4 text:RichText = RichText;"),
    QStringLiteral("textItalic#d912a59c text:RichText = RichText;"),
    QStringLiteral("textUnderline#c12622c4 text:RichText = RichText;"),
    QStringLiteral("textStrike#9bf8bb95 text:RichText = RichText;"),
    QStringLiteral("textFixed#6c3f19b9 text:RichText = RichText;"),
    QStringLiteral("textUrl#3c2884c1 text:RichText url:string webpage_id:long = RichText;"),
    QStringLiteral("textEmail#de5a0dd6 text:RichText email:string = RichText;"),
    QStringLiteral("textConcat#7e6260d7 texts:Vector<RichText> = RichText;"),
};

const QStringList c_sourcesPageBlock =
{
    QStringLiteral("pageBlockUnsupported#13567e8a = PageBlock;"),
    QStringLiteral("pageBlockTitle#70abc3fd text:RichText = PageBlock;"),
    QStringLiteral("pageBlockSubtitle#8ffa9a1f text:RichText = PageBlock;"),
    QStringLiteral("pageBlockList#3a58c7f4 ordered:Bool items:Vector<RichText> = PageBlock;"),
    QStringLiteral("pageBlockEmbedPost#292c7be9 url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector<PageBlock> caption:RichText = PageBlock;"),
    QStringLiteral("pageBlockSlideshow#130c8963 items:Vector<PageBlock> caption:RichText = PageBlock;"),
    QStringLiteral("pageBlockPhoto#e9c69982 photo_id:long caption:RichText = PageBlock;"),
};

class tst_Generator : public QObject
{
    Q_OBJECT
public:
    explicit tst_Generator(QObject *parent = nullptr);

    TLType getSolvedType(const Generator &generator, const QString &typeName) const;

private slots:
    void checkRemoveWord_data();
    void checkRemoveWord();
    void checkTypeWithMemberConflicts();
    void recursiveTypeMembers();
    void doubleRecursiveTypeMembers();
};

tst_Generator::tst_Generator(QObject *parent) :
    QObject(parent)
{
}

TLType tst_Generator::getSolvedType(const Generator &generator, const QString &typeName) const
{
    const QString tName = Generator::formatType(typeName);
    for (const TLType solved : generator.solvedTypes()) {
        if (solved.name == tName) {
            return solved;
        }
    }
    return TLType();
}

void tst_Generator::checkRemoveWord_data()
{
    QTest::addColumn<QString>("input");
    QTest::addColumn<QString>("output");
    QTest::addColumn<QString>("word");

    QTest::newRow("No word in the input")
            << "idInputVideo"
            << "idInputVideo"
            << "audio";
    QTest::newRow("Word in the end (different case)")
            << "idInputVideo"
            << "idInput"
            << "video";
    QTest::newRow("Word in the end (same case)")
            << "idInputVideo"
            << "idInput"
            << "Video";
    QTest::newRow("Word in the middle (different case)")
            << "idInputVideo"
            << "idVideo"
            << "input";
    QTest::newRow("Word in the middle (same case)")
            << "idInputVideo"
            << "idVideo"
            << "Input";
    QTest::newRow("Word in the beginning (different case)")
            << "idInputVideo"
            << "inputVideo"
            << "Id";
    QTest::newRow("Word in the beginning (same case)")
            << "idInputVideo"
            << "inputVideo"
            << "id";
}

void tst_Generator::checkRemoveWord()
{
    QFETCH(QString, input);
    QFETCH(QString, output);
    QFETCH(QString, word);
    QCOMPARE(Generator::removeWord(input, word), output);
}

void tst_Generator::checkTypeWithMemberConflicts()
{
    const QStringList sources = c_sourcesInputMediaDeps + c_sourcesInputMedia;
    const QString generatedTypeName = Generator::parseLine(c_sourcesInputMedia.first()).typeName;
    const QByteArray textData = generateTextSpec(sources);
    Generator generator;
    QVERIFY(generator.loadFromText(textData));
    QVERIFY(generator.resolveTypes());
    QVERIFY(!generator.solvedTypes().isEmpty());
    const TLType solvedType = getSolvedType(generator, generatedTypeName);
    QVERIFY(!solvedType.name.isEmpty());
    const QStringList structMembers = Generator::generateTLTypeMembers(solvedType);
    static const QStringList checkList = {
        QStringLiteral("TLInputFile thumb;"),
        QStringLiteral("TLInputVideo inputVideoId;"),
        QStringLiteral("TLInputAudio inputAudioId;"),
        QStringLiteral("TLVector<TLDocumentAttribute> attributes;"),
    };
    for (const QString &mustHaveMember : checkList) {
        if (!structMembers.contains(mustHaveMember)) {
            // qDebug().noquote() << structMembers.join(QLatin1Char('\n'));
            QString message = QStringLiteral("The member \"%1\" is missing in the generated struct of the type %2.").arg(mustHaveMember, generatedTypeName);
            QFAIL(message.toUtf8().constData());
        }
    }
}

void tst_Generator::recursiveTypeMembers()
{
    const QStringList sources = c_sourcesRichText;
    const QString generatedTypeName = Generator::parseLine(c_sourcesRichText.first()).typeName;
    const QByteArray textData = generateTextSpec(sources);
    Generator generator;
    QVERIFY(generator.loadFromText(textData));
    QMap<QString, TLType> types = generator.types();
    QCOMPARE(types.size(), 1);
    QVERIFY(generator.resolveTypes());
    QVERIFY(!generator.solvedTypes().isEmpty());
    const TLType solvedType = getSolvedType(generator, generatedTypeName);

    QVERIFY(!solvedType.name.isEmpty());
    const QStringList structMembers = Generator::generateTLTypeMembers(solvedType);
    static const QStringList checkList = {
        QStringLiteral("QString email;"),
        QStringLiteral("QString stringText;"),
        QStringLiteral("TLRichTextPtr richText;"),
        QStringLiteral("TLVector<TLRichText*> texts;"),
    };
    for (const QString &mustHaveMember : checkList) {
        if (!structMembers.contains(mustHaveMember)) {
            // qDebug().noquote() << structMembers.join(QLatin1Char('\n'));
            QString message = QStringLiteral("The member \"%1\" is missing in the generated struct of the type %2.").arg(mustHaveMember, generatedTypeName);
            QFAIL(message.toUtf8().constData());
        }
    }
}

void tst_Generator::doubleRecursiveTypeMembers()
{
    const QStringList sources = c_sourcesRichText + c_sourcesPageBlock;
    const QString generatedTypeName = Generator::parseLine(c_sourcesPageBlock.first()).typeName;
    const QByteArray textData = generateTextSpec(sources);
    Generator generator;
    QVERIFY(generator.loadFromText(textData));
    QMap<QString, TLType> types = generator.types();
    QVERIFY(!types.isEmpty());
    QVERIFY(generator.resolveTypes());
    QVERIFY(!generator.solvedTypes().isEmpty());
    const TLType solvedType = getSolvedType(generator, generatedTypeName);
    QVERIFY(!solvedType.name.isEmpty());
    const QStringList structMembers = Generator::generateTLTypeMembers(solvedType);
    static const QStringList checkList = {
        QStringLiteral("TLRichTextPtr text;"),
        QStringLiteral("TLVector<TLRichText*> richTextItemsVector;"),
        QStringLiteral("TLVector<TLPageBlock*> blocks;"),
        QStringLiteral("TLVector<TLPageBlock*> pageBlockItemsVector;"),
        QStringLiteral("TLRichTextPtr caption;"),
    };
    for (const QString &mustHaveMember : checkList) {
        if (!structMembers.contains(mustHaveMember)) {
             qDebug().noquote() << structMembers.join(QLatin1Char('\n'));
            QString message = QStringLiteral("The member \"%1\" is missing in the generated struct of the type %2.").arg(mustHaveMember, generatedTypeName);
            QFAIL(message.toUtf8().constData());
        }
    }
}

QTEST_APPLESS_MAIN(tst_Generator)

#include "tst_generator.moc"
<commit_msg>Tests/Generator: Add test for predicate preprocessing<commit_after>#include <QObject>
#include <QTest>
#include <QDebug>

#include "Generator.hpp"

const QString c_typesSection = QStringLiteral("---types---");
const QString c_functionsSection = QStringLiteral("---functions---");

static QByteArray generateTextSpec(const QStringList &types, const QStringList &functions = {})
{
    QByteArray result;
    if (!types.isEmpty()) {
        const QString textData = c_typesSection + QLatin1Char('\n') + types.join(QLatin1Char('\n')) + QLatin1Char('\n');
        result += textData.toLocal8Bit();
    }
    if (!functions.isEmpty()) {
        const QString textData = c_functionsSection + QLatin1Char('\n') + functions.join(QLatin1Char('\n')) + QLatin1Char('\n');
        result += textData.toLocal8Bit();
    }
    return result;
}

const QStringList c_sourcesInputMediaDeps =
{
    QStringLiteral("inputPhotoEmpty = InputPhoto;"),
    QStringLiteral("inputFileEmpty = InputFile;"),
    QStringLiteral("inputVideoEmpty = InputVideo;"),
    QStringLiteral("inputAudioEmpty = InputAudio;"),
    QStringLiteral("inputDocumentEmpty = InputDocument;"),
    QStringLiteral("documentAttributeEmpty = DocumentAttribute;"),
};

const QStringList c_sourcesInputMedia =
{
    QStringLiteral("inputMediaEmpty#9664f57f = InputMedia;"),
    QStringLiteral("inputMediaUploadedPhoto#f7aff1c0 file:InputFile caption:string = InputMedia;"),
    QStringLiteral("inputMediaPhoto#e9bfb4f3 id:InputPhoto caption:string = InputMedia;"),
    QStringLiteral("inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;"),
    QStringLiteral("inputMediaUploadedVideo#82713fdf file:InputFile duration:int w:int h:int mime_type:string caption:string = InputMedia;"),
    QStringLiteral("inputMediaUploadedThumbVideo#7780ddf9 file:InputFile thumb:InputFile duration:int w:int h:int mime_type:string caption:string = InputMedia;"),
    QStringLiteral("inputMediaVideo#936a4ebd id:InputVideo caption:string = InputMedia;"),
    QStringLiteral("inputMediaUploadedAudio#4e498cab file:InputFile duration:int mime_type:string = InputMedia;"),
    QStringLiteral("inputMediaAudio#89938781 id:InputAudio = InputMedia;"),
    QStringLiteral("inputMediaUploadedDocument#1d89306d file:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string = InputMedia;"),
    QStringLiteral("inputMediaUploadedThumbDocument#ad613491 file:InputFile thumb:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string = InputMedia;"),
    QStringLiteral("inputMediaDocument#1a77f29c id:InputDocument caption:string = InputMedia;"),
};

const QStringList c_sourcesRichText =
{
    QStringLiteral("textEmpty#dc3d824f = RichText;"),
    QStringLiteral("textPlain#744694e0 text:string = RichText;"),
    QStringLiteral("textBold#6724abc4 text:RichText = RichText;"),
    QStringLiteral("textItalic#d912a59c text:RichText = RichText;"),
    QStringLiteral("textUnderline#c12622c4 text:RichText = RichText;"),
    QStringLiteral("textStrike#9bf8bb95 text:RichText = RichText;"),
    QStringLiteral("textFixed#6c3f19b9 text:RichText = RichText;"),
    QStringLiteral("textUrl#3c2884c1 text:RichText url:string webpage_id:long = RichText;"),
    QStringLiteral("textEmail#de5a0dd6 text:RichText email:string = RichText;"),
    QStringLiteral("textConcat#7e6260d7 texts:Vector<RichText> = RichText;"),
};

const QStringList c_sourcesPageBlock =
{
    QStringLiteral("pageBlockUnsupported#13567e8a = PageBlock;"),
    QStringLiteral("pageBlockTitle#70abc3fd text:RichText = PageBlock;"),
    QStringLiteral("pageBlockSubtitle#8ffa9a1f text:RichText = PageBlock;"),
    QStringLiteral("pageBlockList#3a58c7f4 ordered:Bool items:Vector<RichText> = PageBlock;"),
    QStringLiteral("pageBlockEmbedPost#292c7be9 url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector<PageBlock> caption:RichText = PageBlock;"),
    QStringLiteral("pageBlockSlideshow#130c8963 items:Vector<PageBlock> caption:RichText = PageBlock;"),
    QStringLiteral("pageBlockPhoto#e9c69982 photo_id:long caption:RichText = PageBlock;"),
};

class tst_Generator : public QObject
{
    Q_OBJECT
public:
    explicit tst_Generator(QObject *parent = nullptr);

    TLType getSolvedType(const Generator &generator, const QString &typeName) const;

private slots:
    void checkRemoveWord_data();
    void checkRemoveWord();
    void checkTypeWithMemberConflicts();
    void recursiveTypeMembers();
    void doubleRecursiveTypeMembers();
    void predicateForCrc_data();
    void predicateForCrc();
};

tst_Generator::tst_Generator(QObject *parent) :
    QObject(parent)
{
}

TLType tst_Generator::getSolvedType(const Generator &generator, const QString &typeName) const
{
    const QString tName = Generator::formatType(typeName);
    for (const TLType solved : generator.solvedTypes()) {
        if (solved.name == tName) {
            return solved;
        }
    }
    return TLType();
}

void tst_Generator::checkRemoveWord_data()
{
    QTest::addColumn<QString>("input");
    QTest::addColumn<QString>("output");
    QTest::addColumn<QString>("word");

    QTest::newRow("No word in the input")
            << "idInputVideo"
            << "idInputVideo"
            << "audio";
    QTest::newRow("Word in the end (different case)")
            << "idInputVideo"
            << "idInput"
            << "video";
    QTest::newRow("Word in the end (same case)")
            << "idInputVideo"
            << "idInput"
            << "Video";
    QTest::newRow("Word in the middle (different case)")
            << "idInputVideo"
            << "idVideo"
            << "input";
    QTest::newRow("Word in the middle (same case)")
            << "idInputVideo"
            << "idVideo"
            << "Input";
    QTest::newRow("Word in the beginning (different case)")
            << "idInputVideo"
            << "inputVideo"
            << "Id";
    QTest::newRow("Word in the beginning (same case)")
            << "idInputVideo"
            << "inputVideo"
            << "id";
}

void tst_Generator::checkRemoveWord()
{
    QFETCH(QString, input);
    QFETCH(QString, output);
    QFETCH(QString, word);
    QCOMPARE(Generator::removeWord(input, word), output);
}

void tst_Generator::checkTypeWithMemberConflicts()
{
    const QStringList sources = c_sourcesInputMediaDeps + c_sourcesInputMedia;
    const QString generatedTypeName = Generator::parseLine(c_sourcesInputMedia.first()).typeName;
    const QByteArray textData = generateTextSpec(sources);
    Generator generator;
    QVERIFY(generator.loadFromText(textData));
    QVERIFY(generator.resolveTypes());
    QVERIFY(!generator.solvedTypes().isEmpty());
    const TLType solvedType = getSolvedType(generator, generatedTypeName);
    QVERIFY(!solvedType.name.isEmpty());
    const QStringList structMembers = Generator::generateTLTypeMembers(solvedType);
    static const QStringList checkList = {
        QStringLiteral("TLInputFile thumb;"),
        QStringLiteral("TLInputVideo inputVideoId;"),
        QStringLiteral("TLInputAudio inputAudioId;"),
        QStringLiteral("TLVector<TLDocumentAttribute> attributes;"),
    };
    for (const QString &mustHaveMember : checkList) {
        if (!structMembers.contains(mustHaveMember)) {
            // qDebug().noquote() << structMembers.join(QLatin1Char('\n'));
            QString message = QStringLiteral("The member \"%1\" is missing in the generated struct of the type %2.").arg(mustHaveMember, generatedTypeName);
            QFAIL(message.toUtf8().constData());
        }
    }
}

void tst_Generator::recursiveTypeMembers()
{
    const QStringList sources = c_sourcesRichText;
    const QString generatedTypeName = Generator::parseLine(c_sourcesRichText.first()).typeName;
    const QByteArray textData = generateTextSpec(sources);
    Generator generator;
    QVERIFY(generator.loadFromText(textData));
    QMap<QString, TLType> types = generator.types();
    QCOMPARE(types.size(), 1);
    QVERIFY(generator.resolveTypes());
    QVERIFY(!generator.solvedTypes().isEmpty());
    const TLType solvedType = getSolvedType(generator, generatedTypeName);

    QVERIFY(!solvedType.name.isEmpty());
    const QStringList structMembers = Generator::generateTLTypeMembers(solvedType);
    static const QStringList checkList = {
        QStringLiteral("QString email;"),
        QStringLiteral("QString stringText;"),
        QStringLiteral("TLRichTextPtr richText;"),
        QStringLiteral("TLVector<TLRichText*> texts;"),
    };
    for (const QString &mustHaveMember : checkList) {
        if (!structMembers.contains(mustHaveMember)) {
            // qDebug().noquote() << structMembers.join(QLatin1Char('\n'));
            QString message = QStringLiteral("The member \"%1\" is missing in the generated struct of the type %2.").arg(mustHaveMember, generatedTypeName);
            QFAIL(message.toUtf8().constData());
        }
    }
}

void tst_Generator::doubleRecursiveTypeMembers()
{
    const QStringList sources = c_sourcesRichText + c_sourcesPageBlock;
    const QString generatedTypeName = Generator::parseLine(c_sourcesPageBlock.first()).typeName;
    const QByteArray textData = generateTextSpec(sources);
    Generator generator;
    QVERIFY(generator.loadFromText(textData));
    QMap<QString, TLType> types = generator.types();
    QVERIFY(!types.isEmpty());
    QVERIFY(generator.resolveTypes());
    QVERIFY(!generator.solvedTypes().isEmpty());
    const TLType solvedType = getSolvedType(generator, generatedTypeName);
    QVERIFY(!solvedType.name.isEmpty());
    const QStringList structMembers = Generator::generateTLTypeMembers(solvedType);
    static const QStringList checkList = {
        QStringLiteral("TLRichTextPtr text;"),
        QStringLiteral("TLVector<TLRichText*> richTextItemsVector;"),
        QStringLiteral("TLVector<TLPageBlock*> blocks;"),
        QStringLiteral("TLVector<TLPageBlock*> pageBlockItemsVector;"),
        QStringLiteral("TLRichTextPtr caption;"),
    };
    for (const QString &mustHaveMember : checkList) {
        if (!structMembers.contains(mustHaveMember)) {
             qDebug().noquote() << structMembers.join(QLatin1Char('\n'));
            QString message = QStringLiteral("The member \"%1\" is missing in the generated struct of the type %2.").arg(mustHaveMember, generatedTypeName);
            QFAIL(message.toUtf8().constData());
        }
    }
}

void tst_Generator::predicateForCrc_data()
{
    QTest::addColumn<QByteArray>("input");
    QTest::addColumn<QByteArray>("output");

    QTest::addRow("Trivial") << QByteArrayLiteral("help.getConfig#c4f9186b = Config;")
                             << QByteArrayLiteral("help.getConfig = Config");

    QTest::addRow("Trivial") << QByteArrayLiteral("req_pq#60469778 nonce:int128 = ResPQ;")
                             << QByteArrayLiteral("req_pq nonce:int128 = ResPQ");

    QTest::addRow("With true flags") << QByteArrayLiteral("channels.createChannel#f4893d7f flags:#"
                                                          " broadcast:flags.0?true megagroup:flags.1?true"
                                                          " title:string about:string = Updates;")
                                     << QByteArrayLiteral("channels.createChannel flags:#"
                                                          " title:string about:string = Updates");

    QTest::addRow("With flags") << QByteArrayLiteral("updates.getDifference#25939651 flags:#"
                                                     " pts:int pts_total_limit:flags.0?int date:int"
                                                     " qts:int = updates.Difference;")
                                << QByteArrayLiteral("updates.getDifference flags:#"
                                                     " pts:int pts_total_limit:flags.0?int date:int"
                                                     " qts:int = updates.Difference");

    QTest::addRow("With vector") << QByteArrayLiteral("messages.getAllChats#eba80ff0"
                                                      " except_ids:Vector<int> = messages.Chats;")
                                 << QByteArrayLiteral("messages.getAllChats"
                                                      " except_ids:Vector int = messages.Chats");

    QTest::addRow("Vector") << QByteArrayLiteral("vector#1cb5c415 {t:Type} # [ t ] = Vector t;")
                            << QByteArrayLiteral("vector t:Type # [ t ] = Vector t");
}

void tst_Generator::predicateForCrc()
{
    QFETCH(QByteArray, input);
    QFETCH(QByteArray, output);
    QCOMPARE(Generator::getPredicateForCrc32(input), output);
}

QTEST_APPLESS_MAIN(tst_Generator)

#include "tst_generator.moc"
<|endoftext|>


<commit_before>/***********************************
 Copyright 2018 Ravishankar Mathur

 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 <OpenFrames/OpenVRDevice.hpp>
#include <OpenFrames/ReferenceFrame.hpp>
#include <osg/Matrixd>
#include <osg/Notify>
#include <osg/MatrixTransform>

// Assume 3 stub tracked devices here: HMD + 2 base stations
const unsigned int numTrackedDevices = 3;

namespace OpenFrames{

  /*************************************************************/
  OpenVREvent::VREvent::VREvent()
  : _ovrEvent(NULL), _controllerState(NULL)
  {}

  /*************************************************************/
  OpenVREvent::VREvent::~VREvent()
  {}

  /*************************************************************/
  bool OpenVRDevice::initVR()
  {
    osg::notify(osg::NOTICE) << "Using OpenVR stub" << std::endl;

    // Set texture size similar to what OpenVR would return
    _width = 1512;  // 1.4*1080
    _height = 1680; // 1.4*1200
    osg::notify(osg::NOTICE) << "VR eye texture width = " << _width << ", height = " << _height << std::endl;

    // Update the per-eye projection matrices
    // The view offset matrices will be computed per-frame since IPD can change
    updateProjectionMatrices();

    // Allocate render data for each possible tracked device
    // The render data struct is a light wrapper, so there is no size concern here
    _deviceIDToModel.resize(numTrackedDevices);

    // Get render models for controllers and other devices
    createDeviceRenderModels();

    return (_isInitialized = true);
  }

  /*************************************************************/
  void OpenVRDevice::shutdownVR()
  {
    _deviceNameToGeode.clear();
    _deviceIDToModel.clear();
    _deviceModels->removeChildren(0, _deviceModels->getNumChildren());
    _isInitialized = false;
  }

  /*************************************************************/
  void OpenVRDevice::createDeviceRenderModels()
  {
    // Loop through all possible tracked devices except the HMD (already loaded)
    for(unsigned int deviceID = 0; deviceID < numTrackedDevices; ++deviceID)
    {
      // Set up device render model
      setupRenderModelForTrackedDevice(deviceID);
    }
  }

  /*************************************************************/
  void OpenVRDevice::setupRenderModelForTrackedDevice(uint32_t deviceID)
  {
    // Get name of tracked device
    std::string deviceName;
    if(deviceID == 0) deviceName = "HMD_Stub";
    else deviceName = "BaseStation_Stub";

    // Find device data by name
    DeviceGeodeMap::iterator i = _deviceNameToGeode.find(deviceName);

    // If not found, then load device data
    if(i == _deviceNameToGeode.end())
    {
      osg::notify(osg::NOTICE) << "OpenFrames::OpenVRDeviceStub: Setting up render data for device " << deviceName << std::endl;

      float radius = 0.1f;
      float height = 0.2f;

      // Create device model's render model and add it to the render group
      osg::Geode *geode = new osg::Geode;
      geode->addDrawable(new osg::ShapeDrawable(new osg::Capsule(osg::Vec3(),radius, height)));
      _deviceNameToGeode[deviceName] = geode;
    }

    // Set up device model if needed
    if(_deviceIDToModel[deviceID]._modelTransform->getNumChildren() == 0)
    {
      osg::notify(osg::NOTICE) << "OpenFrames::OpenVRDeviceStub: Setting up transform for device " << deviceName << deviceID << std::endl;

      // Add device model's transform to the group of all rendered devices
      _deviceIDToModel[deviceID]._modelTransform->addChild(_deviceNameToGeode[deviceName]);
      _deviceModels->addChild(_deviceIDToModel[deviceID]._modelTransform);

      // Set device class
      if(deviceID == 0) _deviceIDToModel[deviceID]._class = HMD;
      else _deviceIDToModel[deviceID]._class = BASESTATION;
    }
  }

  /*************************************************************/
  void OpenVRDevice::updateProjectionMatrices()
  {
    // Create right/left/center projection matrices. Using unit depth minimizes
    // precision losses in the projection matrix
    _rightEyeProj.makePerspective(110.0, (double)_width/(double)_height, 1.0, 2.0);
    _leftEyeProj.makePerspective(110.0, (double)_width/(double)_height, 1.0, 2.0);

    // Center projection is average of right and left
    // OSG doesn't have a matrix addition for Matrixd (facepalm)
    osg::Matrixf rightEyeProjf = _rightEyeProj;
    osg::Matrixf leftEyeProjf = _leftEyeProj;
    _centerProj = (rightEyeProjf + leftEyeProjf)*0.5;
  }

  /*************************************************************/
  void OpenVRDevice::updateViewOffsets()
  {
    // Simulate raw left/right eye vectors relative to HMD origin
    _rightEyeViewOffsetRaw.set(0.03, 0.01, -0.01);
    _leftEyeViewOffsetRaw.set(-0.03, 0.01, -0.01);

    // Compute HMD center view offset vector (Eye to Head transform)
    _centerViewOffsetRaw = (_rightEyeViewOffsetRaw + _leftEyeViewOffsetRaw)*0.5;
    _rightEyeViewOffsetRaw -= _centerViewOffsetRaw;
    _leftEyeViewOffsetRaw -= _centerViewOffsetRaw;
  }

  /*************************************************************/
  void OpenVRDevice::waitGetPoses()
  {
    osg::Matrixf matDeviceToWorld; // Device to World transform

    // Simulate poses for all VR devices
    for(unsigned int i = 0; i < numTrackedDevices; ++i)
    {
      _deviceIDToModel[i]._valid = true; // Always valid

      // Set simulated HMD or base station pose in meters
      if(i == 0)
      {
        matDeviceToWorld.makeIdentity();
        matDeviceToWorld(3, 1) = 1.6764; // 5'6" simulated user height in meters
      }
      else if(i == 1)
      {
        matDeviceToWorld.makeRotate(osg::Quat(10.0, osg::Vec3d(1, 0, 0)));
        matDeviceToWorld.postMultTranslate(osg::Vec3d(-3, 3, -3));
      }
      else if(i == 2)
      {
        matDeviceToWorld.makeRotate(osg::Quat(10.0, osg::Vec3d(0, 1, 0)));
        matDeviceToWorld.postMultTranslate(osg::Vec3d(2, 3, -2));
      }

      matDeviceToWorld(3, 1) -= _userHeight; // Subtract user's height, OpenVR world is Y-up
      _deviceIDToModel[i]._rawDeviceToWorld = matDeviceToWorld;

      // Enable device model's transform so that it will be rendered
      // Only enable base stations, since we don't want to render anything else
      if(_deviceIDToModel[i]._class == BASESTATION)
        _deviceIDToModel[i]._modelTransform->setNodeMask(0xffffffff);
      else
        _deviceIDToModel[i]._modelTransform->setNodeMask(0x0);
    }

    // Update the HMD->Eye view offset vectors
    updateViewOffsets();
  }

  /*************************************************************/
  void OpenVRDevice::submitFrame(GLuint rightEyeTexName, GLuint leftEyeTexName)
  {
    // Nothing to do here
  }

  /*************************************************************/
  bool OpenVRDevice::pollNextEvent(OpenVREvent *event)
  {
    // Nothing to do here
    return false;
  }

  /*************************************************************/
  bool OpenVREventDevice::checkEvents()
  {
    // Nothing to do here
    return false;
  }

  /*************************************************************/
  bool OpenVRTrackball::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
  {
    // Compute new motion-based view at each frame
    if (ea.getEventType() == osgGA::GUIEventAdapter::FRAME)
      processMotion();

    // Just call parent trackball handler
    return FollowingTrackball::handle(ea, us);
  }

  /*************************************************************/
  bool OpenVRImageHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, osg::Object* obj, osg::NodeVisitor* nv)
  {
    // Compute new motion-based view at each frame
    if (ea.getEventType() == osgGA::GUIEventAdapter::FRAME)
      processImagePick(nv->getFrameStamp()->getReferenceTime());

    // Just call parent trackball handler
    return osgViewer::InteractiveImageHandler::handle(ea, aa, obj, nv);
  }

} // !namespace OpenFrames
<commit_msg>Stub VR compatibility updates<commit_after>/***********************************
 Copyright 2018 Ravishankar Mathur

 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 <OpenFrames/OpenVRDevice.hpp>
#include <OpenFrames/ReferenceFrame.hpp>
#include <osg/Matrixd>
#include <osg/Notify>
#include <osg/MatrixTransform>

// Assume 3 stub tracked devices here: HMD + 2 base stations
const unsigned int numTrackedDevices = 3;

namespace OpenFrames{

  /*************************************************************/
  OpenVREvent::OpenVREvent()
  : _ovrEvent(NULL)
  {
    setEventType(osgGA::GUIEventAdapter::USER);
  }

  /*************************************************************/
  OpenVREvent::~OpenVREvent()
  {}

  /*************************************************************/
  bool OpenVRDevice::initVR()
  {
    osg::notify(osg::NOTICE) << "Using OpenVR stub" << std::endl;

    // Set texture size similar to what OpenVR would return
    _width = 1512;  // 1.4*1080
    _height = 1680; // 1.4*1200
    osg::notify(osg::NOTICE) << "VR eye texture width = " << _width << ", height = " << _height << std::endl;

    // Update the per-eye projection matrices
    // The view offset matrices will be computed per-frame since IPD can change
    updateProjectionMatrices();

    // Allocate render data for each possible tracked device
    // The render data struct is a light wrapper, so there is no size concern here
    _deviceIDToModel.resize(numTrackedDevices);

    // Get render models for controllers and other devices
    createDeviceRenderModels();

    return (_isInitialized = true);
  }

  /*************************************************************/
  void OpenVRDevice::shutdownVR()
  {
    _deviceNameToGeode.clear();
    _deviceIDToModel.clear();
    _deviceModels->removeChildren(0, _deviceModels->getNumChildren());
    _isInitialized = false;
  }

  /*************************************************************/
  OpenVRDevice::DeviceModel::~DeviceModel() { }

  /*************************************************************/
  void OpenVRDevice::updateDeviceModels() { }

  /*************************************************************/
  void OpenVRDevice::createDeviceRenderModels()
  {
    // Loop through all possible tracked devices except the HMD (already loaded)
    for(unsigned int deviceID = 0; deviceID < numTrackedDevices; ++deviceID)
    {
      // Set up device render model
      setupRenderModelForTrackedDevice(deviceID);
    }
  }

  /*************************************************************/
  void OpenVRDevice::setupRenderModelForTrackedDevice(uint32_t deviceID)
  {
    // Get name of tracked device
    std::string deviceName;
    if(deviceID == 0) deviceName = "HMD_Stub";
    else deviceName = "BaseStation_Stub";

    // Find device data by name
    DeviceGeodeMap::iterator i = _deviceNameToGeode.find(deviceName);

    // If not found, then load device data
    if(i == _deviceNameToGeode.end())
    {
      osg::notify(osg::NOTICE) << "OpenFrames::OpenVRDeviceStub: Setting up render data for device " << deviceName << std::endl;

      float radius = 0.1f;
      float height = 0.2f;

      // Create device model's render model and add it to the render group
      osg::Geode *geode = new osg::Geode;
      geode->addDrawable(new osg::ShapeDrawable(new osg::Capsule(osg::Vec3(),radius, height)));
      _deviceNameToGeode[deviceName] = geode;
    }

    // Set up device model if needed
    if(_deviceIDToModel[deviceID]._modelTransform->getNumChildren() == 0)
    {
      osg::notify(osg::NOTICE) << "OpenFrames::OpenVRDeviceStub: Setting up transform for device " << deviceName << deviceID << std::endl;

      // Add device model's transform to the group of all rendered devices
      _deviceIDToModel[deviceID]._modelTransform->addChild(_deviceNameToGeode[deviceName]);
      _deviceModels->addChild(_deviceIDToModel[deviceID]._modelTransform);

      // Set device class
      if(deviceID == 0) _deviceIDToModel[deviceID]._class = HMD;
      else _deviceIDToModel[deviceID]._class = BASESTATION;
    }
  }

  /*************************************************************/
  void OpenVRDevice::updateProjectionMatrices()
  {
    // Create right/left/center projection matrices. Using unit depth minimizes
    // precision losses in the projection matrix
    _rightEyeProj.makePerspective(110.0, (double)_width/(double)_height, 1.0, 2.0);
    _leftEyeProj.makePerspective(110.0, (double)_width/(double)_height, 1.0, 2.0);

    // Center projection is average of right and left
    // OSG doesn't have a matrix addition for Matrixd (facepalm)
    osg::Matrixf rightEyeProjf = _rightEyeProj;
    osg::Matrixf leftEyeProjf = _leftEyeProj;
    _centerProj = (rightEyeProjf + leftEyeProjf)*0.5;
  }

  /*************************************************************/
  void OpenVRDevice::updateViewOffsets()
  {
    // Simulate raw left/right eye vectors relative to HMD origin
    _rightEyeViewOffsetRaw.set(0.03, 0.01, -0.01);
    _leftEyeViewOffsetRaw.set(-0.03, 0.01, -0.01);

    // Compute HMD center view offset vector (Eye to Head transform)
    _centerViewOffsetRaw = (_rightEyeViewOffsetRaw + _leftEyeViewOffsetRaw)*0.5;
    _rightEyeViewOffsetRaw -= _centerViewOffsetRaw;
    _leftEyeViewOffsetRaw -= _centerViewOffsetRaw;
  }

  /*************************************************************/
  void OpenVRDevice::waitGetPoses()
  {
    osg::Matrixf matDeviceToWorld; // Device to World transform

    // Simulate poses for all VR devices
    for(unsigned int i = 0; i < numTrackedDevices; ++i)
    {
      _deviceIDToModel[i]._valid = true; // Always valid

      // Set simulated HMD or base station pose in meters
      if(i == 0)
      {
        matDeviceToWorld.makeIdentity();
        matDeviceToWorld(3, 1) = 1.6764; // 5'6" simulated user height in meters
      }
      else if(i == 1)
      {
        matDeviceToWorld.makeRotate(osg::Quat(10.0, osg::Vec3d(1, 0, 0)));
        matDeviceToWorld.postMultTranslate(osg::Vec3d(-3, 3, -3));
      }
      else if(i == 2)
      {
        matDeviceToWorld.makeRotate(osg::Quat(10.0, osg::Vec3d(0, 1, 0)));
        matDeviceToWorld.postMultTranslate(osg::Vec3d(2, 3, -2));
      }

      matDeviceToWorld(3, 1) -= _userHeight; // Subtract user's height, OpenVR world is Y-up
      _deviceIDToModel[i]._rawDeviceToWorld = matDeviceToWorld;

      // Enable device model's transform so that it will be rendered
      // Only enable base stations, since we don't want to render anything else
      if(_deviceIDToModel[i]._class == BASESTATION)
        _deviceIDToModel[i]._modelTransform->setNodeMask(0xffffffff);
      else
        _deviceIDToModel[i]._modelTransform->setNodeMask(0x0);
    }

    // Update the HMD->Eye view offset vectors
    updateViewOffsets();
  }

  /*************************************************************/
  void OpenVRDevice::submitFrame(GLuint rightEyeTexName, GLuint leftEyeTexName)
  {
    // Nothing to do here
  }

  /*************************************************************/
  bool OpenVRDevice::pollNextEvent(OpenVREvent *event)
  {
    // Nothing to do here
    return false;
  }

  /*************************************************************/
  bool OpenVREventDevice::checkEvents()
  {
    // Nothing to do here
    return false;
  }

  /*************************************************************/
  bool OpenVRTrackball::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
  {
    // Compute new motion-based view at each frame
    if (ea.getEventType() == osgGA::GUIEventAdapter::FRAME)
      processMotion();

    // Just call parent trackball handler
    return FollowingTrackball::handle(ea, us);
  }

  /*************************************************************/
  bool OpenVRImageHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, osg::Object* obj, osg::NodeVisitor* nv)
  {
    // Compute new motion-based view at each frame
    if (ea.getEventType() == osgGA::GUIEventAdapter::FRAME)
      processImagePick();

    // Just call parent trackball handler
    return osgViewer::InteractiveImageHandler::handle(ea, aa, obj, nv);
  }

  /*************************************************************/
  float OpenVRImageHandler::getTriggerValue(const vr::VRControllerState_t *controllerState) const
  {
    return 0.0;
  }

} // !namespace OpenFrames
<|endoftext|>


<commit_before>
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Burak Arslan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <behold.h>

/*
template <>
LogEntry<T> &LogEntry<T>::operator<<(const std::vector<int> &v) {
    auto size = v.size();
    std::stringstream s;
    if (size < VECTOR_MAX_SIZE) {
        s << "[";

        int i = 0;
        for (const auto &t: v) {
            if (i++ > 0) {
                s << ", ";
            }
            s << t;
        }
        s << "]";
    }
    else {
        s << "std::vector<int>(len=" << v.size() << ")";
    }
    m_line.push_back(s.str());
    return *this;
}

template <>
LogEntry &LogEntry<T>::operator<<(const std::vector<std::string> &v) {
    auto size = v.size();
    std::stringstream s;
    if (size < VECTOR_MAX_SIZE) {
        s << "[";

        int i = 0;
        for (const auto &t: v) {
            if (i++ > 0) {
                s << ", ";
            }
            s << "\"" << t << "\"";
        }
        s << "]";
    }
    else {
        s << "std::vector<std::string>(len=" << v.size() << ")";
    }
    m_line.push_back(s.str());
    return *this;
}

template <>
LogEntry &LogEntry<T>::operator<<(const std::set<int> &s) {
    auto size = s.size();
    std::stringstream sstr;
    if (size < SET_MAX_SIZE) {
        sstr << "{";

        int i = 0;
        for (const auto &t: s) {
            if (i++ > 0) {
                sstr << ", ";
            }
            sstr << t;
        }
        sstr << "}";
    }
    else {
        sstr << "std::set<len=" << s.size() << ">";
    }
    m_line.push_back(sstr.str());
    return *this;
}
*/
<commit_msg>partial nested template specializations don't work. *sigh*<commit_after>
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Burak Arslan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <behold.h>

/*

template <typename S, S &out>
template<>
//       ^
// error: invalid explicit specialization before ‘>’ token
LogEntry<S, out> &LogEntry<S, out>::operator<<(const std::vector<int> &v) {
    auto size = v.size();
    std::stringstream s;
    if (size < VECTOR_MAX_SIZE) {
        s << "[";

        int i = 0;
        for (const auto &t: v) {
            if (i++ > 0) {
                s << ", ";
            }
            s << t;
        }
        s << "]";
    }
    else {
        s << "std::vector<int>(len=" << v.size() << ")";
    }
    m_line.push_back(s.str());
    return *this;
}

template <typename S, S &out>
template<>
LogEntry<S, out> &LogEntry<S, out>::operator<<(const std::vector<std::string> &v) {
    auto size = v.size();
    std::stringstream s;
    if (size < VECTOR_MAX_SIZE) {
        s << "[";

        int i = 0;
        for (const auto &t: v) {
            if (i++ > 0) {
                s << ", ";
            }
            s << "\"" << t << "\"";
        }
        s << "]";
    }
    else {
        s << "std::vector<std::string>(len=" << v.size() << ")";
    }
    m_line.push_back(s.str());
    return *this;
}

template <typename S, S &out>
template<>
LogEntry<S, out> &LogEntry<S, out>::operator<<(const std::set<int> &s) {
    auto size = s.size();
    std::stringstream sstr;
    if (size < SET_MAX_SIZE) {
        sstr << "{";

        int i = 0;
        for (const auto &t: s) {
            if (i++ > 0) {
                sstr << ", ";
            }
            sstr << t;
        }
        sstr << "}";
    }
    else {
        sstr << "std::set<len=" << s.size() << ">";
    }
    m_line.push_back(sstr.str());
    return *this;
}

*/
<|endoftext|>