<commit_before>/* This file is part of the KDE project
   Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
   Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
   Copyright (C) 2002 Anders Lund <anders.lund@lund.tdcadsl.dk>
   Copyright (C) 2007 Anders Lund <anders@alweb.dk>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

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

#include "kateconsole.h"
#include "kateconsole.moc"

#include <kicon.h>
#include <kiconloader.h>
#include <ktexteditor/document.h>
#include <ktexteditor/view.h>

#include <kde_terminal_interface.h>
#include <kshell.h>
#include <kparts/part.h>
#include <kaction.h>
#include <kactioncollection.h>
#include <KDialog>

#include <kurl.h>
#include <klibloader.h>
#include <klocale.h>
#include <kdebug.h>
#include <kmessagebox.h>
//Added by qt3to4:
#include <QShowEvent>

#include <QApplication>
#include <QCheckBox>
#include <QVBoxLayout>

#include <kgenericfactory.h>
#include <kpluginfactory.h>
#include <kauthorized.h>

K_EXPORT_COMPONENT_FACTORY( katekonsoleplugin, KGenericFactory<KateKonsolePlugin>( "katekonsoleplugin" ) )

KateKonsolePlugin::KateKonsolePlugin( QObject* parent, const QStringList& ):
    Kate::Plugin ( (Kate::Application*)parent )
{
  if (!KAuthorized::authorizeKAction("shell_access"))
  {
    KMessageBox::sorry(0, i18n ("You do not have enough karma to access a shell or terminal emulation"));
  }
}

Kate::PluginView *KateKonsolePlugin::createView (Kate::MainWindow *mainWindow)
{
  KateKonsolePluginView *view = new KateKonsolePluginView (mainWindow);
  mViews.append( view );
  return view;
}

Kate::PluginConfigPage *KateKonsolePlugin::configPage (uint number, QWidget *parent, const char *name)
{
  Q_UNUSED(name)
  if (number != 0)
    return 0;
  return new KateKonsoleConfigPage(parent, this);
}

QString KateKonsolePlugin::configPageName (uint number) const
{
  if (number != 0) return QString();
  return i18n("Terminal");
}

QString KateKonsolePlugin::configPageFullName (uint number) const
{
  if (number != 0) return QString();
  return i18n("Terminal settings");
}

KIcon KateKonsolePlugin::configPageIcon (uint number) const
{
  if (number != 0) return KIcon();
  return KIcon("terminal");
}

void KateKonsolePlugin::readConfig()
{
  foreach ( KateKonsolePluginView *view, mViews )
    view->readConfig();
}

KateKonsolePluginView::KateKonsolePluginView (Kate::MainWindow *mainWindow)
    : Kate::PluginView (mainWindow)
{
  // init console
  QWidget *toolview = mainWindow->createToolView ("kate_private_plugin_katekonsoleplugin", Kate::MainWindow::Bottom, SmallIcon("konsole"), i18n("Terminal"));
  m_console = new KateConsole(mainWindow, toolview);
}

KateKonsolePluginView::~KateKonsolePluginView ()
{
  // cleanup, kill toolview + console
  QWidget *toolview = m_console->parentWidget();
  delete m_console;
  delete toolview;
}

void KateKonsolePluginView::readConfig()
{
  m_console->readConfig();
}

KateConsole::KateConsole (Kate::MainWindow *mw, QWidget *parent)
    : KVBox (parent), KXMLGUIClient()
    , m_part (0)
    , m_mw (mw)
    , m_toolView (parent)
{
  QAction* a = actionCollection()->addAction("katekonsole_tools_pipe_to_terminal");
  a->setIcon(KIcon("pipe"));
  a->setText(i18n("&Pipe to Terminal"));
  connect(a, SIGNAL(triggered()), this, SLOT(slotPipeToConsole()));

  a = actionCollection()->addAction("katekonsole_tools_sync");
  a->setText(i18n("S&yncronize Terminal with Current Document"));
  connect(a, SIGNAL(triggered()), this, SLOT(slotManualSync()));

  a = actionCollection()->addAction("katekonsole_tools_toggle_focus");
  a->setIcon(KIcon("terminal"));
  a->setText(i18n("&Focus Terminal"));
  connect(a, SIGNAL(triggered()), this, SLOT(slotToggleFocus()));

  setComponentData (KComponentData("kate"));
  setXMLFile("plugins/katekonsole/ui.rc");
  m_mw->guiFactory()->addClient (this);

  readConfig();
}

KateConsole::~KateConsole ()
{
  m_mw->guiFactory()->removeClient (this);
  if (m_part)
    disconnect ( m_part, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) );
}

void KateConsole::loadConsoleIfNeeded()
{
  if (m_part) return;

  if (!window() || !parentWidget()) return;
  if (!window() || !isVisibleTo(window())) return;

  KPluginFactory *factory = KPluginLoader("libkonsolepart").factory();

  if (!factory) return;

  m_part = static_cast<KParts::ReadOnlyPart *>(factory->create<QObject>(this, this));

  if (!m_part) return;

  // start the terminal
  qobject_cast<TerminalInterface*>(m_part)->showShellInDir( QString() );

  KGlobal::locale()->insertCatalog("konsole");

  setFocusProxy(m_part->widget());
  m_part->widget()->show();

  connect ( m_part, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) );

  slotSync();
}

void KateConsole::slotDestroyed ()
{
  m_part = 0;

  // hide the dockwidget
  if (parentWidget())
  {
    m_mw->hideToolView (m_toolView);
    m_mw->centralWidget()->setFocus ();
  }
}

void KateConsole::showEvent(QShowEvent *)
{
  if (m_part) return;

  loadConsoleIfNeeded();
}

void KateConsole::cd (const KUrl &url)
{
  sendInput("cd " + KShell::quoteArg(url.path()) + '\n');
}

void KateConsole::sendInput( const QString& text )
{
  loadConsoleIfNeeded();

  if (!m_part) return;

  TerminalInterface *t = qobject_cast<TerminalInterface *>(m_part);

  if (!t) return;

  t->sendInput (text);
}

void KateConsole::slotPipeToConsole ()
{
  if (KMessageBox::warningContinueCancel
      (m_mw->window()
       , i18n ("Do you really want to pipe the text to the console? This will execute any contained commands with your user rights.")
       , i18n ("Pipe to Terminal?")
       , KGuiItem(i18n("Pipe to Terminal")), KStandardGuiItem::cancel(), "Pipe To Terminal Warning") != KMessageBox::Continue)
    return;

  KTextEditor::View *v = m_mw->activeView();

  if (!v)
    return;

  if (v->selection())
    sendInput (v->selectionText());
  else
    sendInput (v->document()->text());
}

void KateConsole::slotSync()
{
  if (m_mw->activeView() ) {
    if ( m_mw->activeView()->document()->url().isValid()
      && m_mw->activeView()->document()->url().isLocalFile() ) {
      cd(KUrl( m_mw->activeView()->document()->url().directory() ));
    } else if ( !m_mw->activeView()->document()->url().isEmpty() ) {
      sendInput( "### " + i18n("Sorry, can not cd into '%1'", m_mw->activeView()->document()->url().directory() ) + "\n" );
    }
  }
}

void KateConsole::slotManualSync()
{
  slotSync();
  if ( ! m_part || ! m_part->widget()->isVisible() )
    m_mw->showToolView( parentWidget() );
}
void KateConsole::slotToggleFocus()
{
  QAction *action = actionCollection()->action("katekonsole_tools_toggle_focus");
  if ( ! m_part ) {
    m_mw->showToolView( parentWidget() );
    action->setText( i18n("Defocus Terminal") );
    return; // this shows and focuses the konsole
  }

  if ( ! m_part ) return;

  if (m_part->widget()->hasFocus()) {
    if (m_mw->activeView())
      m_mw->activeView()->setFocus();
      action->setText( i18n("Focus Terminal") );
  } else {
    // show the view if it is hidden
    if (parentWidget()->isHidden())
      m_mw->showToolView( parentWidget() );
    else // should focus the widget too!
      m_part->widget()->setFocus( Qt::OtherFocusReason );
    action->setText( i18n("Defocus Terminal") );
  }
}

void KateConsole::readConfig()
{
  if ( KConfigGroup(KGlobal::config(), "Konsole").readEntry("AutoSyncronize", false) )
    connect( m_mw, SIGNAL(viewChanged()), SLOT(slotSync()) );
  else
    disconnect( m_mw, SIGNAL(viewChanged()), this, SLOT(slotSync()) );
}

KateKonsoleConfigPage::KateKonsoleConfigPage( QWidget* parent, KateKonsolePlugin *plugin )
  : Kate::PluginConfigPage( parent )
  , mPlugin( plugin )
{
  QVBoxLayout *lo = new QVBoxLayout( this );
  lo->setSpacing( KDialog::spacingHint() );

  cbAutoSyncronize = new QCheckBox( i18n("&Automatically syncronize the terminal with the current document when possible"), this );
  lo->addWidget( cbAutoSyncronize );
  reset();
  lo->addStretch();
  connect( cbAutoSyncronize, SIGNAL(stateChanged(int)), SIGNAL(changed()) );
}

void KateKonsoleConfigPage::apply()
{
  KConfigGroup config(KGlobal::config(), "Konsole");
  config.writeEntry("AutoSyncronize", cbAutoSyncronize->isChecked());
  config.sync();
  mPlugin->readConfig();
}

void KateKonsoleConfigPage::reset()
{
  KConfigGroup config(KGlobal::config(), "Konsole");
  cbAutoSyncronize->setChecked(config.readEntry("AutoSyncronize", false));
}

// kate: space-indent on; indent-width 2; replace-tabs on;

<commit_msg>fix icon name<commit_after>/* This file is part of the KDE project
   Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
   Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
   Copyright (C) 2002 Anders Lund <anders.lund@lund.tdcadsl.dk>
   Copyright (C) 2007 Anders Lund <anders@alweb.dk>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

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

#include "kateconsole.h"
#include "kateconsole.moc"

#include <kicon.h>
#include <kiconloader.h>
#include <ktexteditor/document.h>
#include <ktexteditor/view.h>

#include <kde_terminal_interface.h>
#include <kshell.h>
#include <kparts/part.h>
#include <kaction.h>
#include <kactioncollection.h>
#include <KDialog>

#include <kurl.h>
#include <klibloader.h>
#include <klocale.h>
#include <kdebug.h>
#include <kmessagebox.h>
//Added by qt3to4:
#include <QShowEvent>

#include <QApplication>
#include <QCheckBox>
#include <QVBoxLayout>

#include <kgenericfactory.h>
#include <kpluginfactory.h>
#include <kauthorized.h>

K_EXPORT_COMPONENT_FACTORY( katekonsoleplugin, KGenericFactory<KateKonsolePlugin>( "katekonsoleplugin" ) )

KateKonsolePlugin::KateKonsolePlugin( QObject* parent, const QStringList& ):
    Kate::Plugin ( (Kate::Application*)parent )
{
  if (!KAuthorized::authorizeKAction("shell_access"))
  {
    KMessageBox::sorry(0, i18n ("You do not have enough karma to access a shell or terminal emulation"));
  }
}

Kate::PluginView *KateKonsolePlugin::createView (Kate::MainWindow *mainWindow)
{
  KateKonsolePluginView *view = new KateKonsolePluginView (mainWindow);
  mViews.append( view );
  return view;
}

Kate::PluginConfigPage *KateKonsolePlugin::configPage (uint number, QWidget *parent, const char *name)
{
  Q_UNUSED(name)
  if (number != 0)
    return 0;
  return new KateKonsoleConfigPage(parent, this);
}

QString KateKonsolePlugin::configPageName (uint number) const
{
  if (number != 0) return QString();
  return i18n("Terminal");
}

QString KateKonsolePlugin::configPageFullName (uint number) const
{
  if (number != 0) return QString();
  return i18n("Terminal settings");
}

KIcon KateKonsolePlugin::configPageIcon (uint number) const
{
  if (number != 0) return KIcon();
  return KIcon("utilities-terminal");
}

void KateKonsolePlugin::readConfig()
{
  foreach ( KateKonsolePluginView *view, mViews )
    view->readConfig();
}

KateKonsolePluginView::KateKonsolePluginView (Kate::MainWindow *mainWindow)
    : Kate::PluginView (mainWindow)
{
  // init console
  QWidget *toolview = mainWindow->createToolView ("kate_private_plugin_katekonsoleplugin", Kate::MainWindow::Bottom, SmallIcon("konsole"), i18n("Terminal"));
  m_console = new KateConsole(mainWindow, toolview);
}

KateKonsolePluginView::~KateKonsolePluginView ()
{
  // cleanup, kill toolview + console
  QWidget *toolview = m_console->parentWidget();
  delete m_console;
  delete toolview;
}

void KateKonsolePluginView::readConfig()
{
  m_console->readConfig();
}

KateConsole::KateConsole (Kate::MainWindow *mw, QWidget *parent)
    : KVBox (parent), KXMLGUIClient()
    , m_part (0)
    , m_mw (mw)
    , m_toolView (parent)
{
  QAction* a = actionCollection()->addAction("katekonsole_tools_pipe_to_terminal");
  a->setIcon(KIcon("pipe"));
  a->setText(i18n("&Pipe to Terminal"));
  connect(a, SIGNAL(triggered()), this, SLOT(slotPipeToConsole()));

  a = actionCollection()->addAction("katekonsole_tools_sync");
  a->setText(i18n("S&yncronize Terminal with Current Document"));
  connect(a, SIGNAL(triggered()), this, SLOT(slotManualSync()));

  a = actionCollection()->addAction("katekonsole_tools_toggle_focus");
  a->setIcon(KIcon("terminal"));
  a->setText(i18n("&Focus Terminal"));
  connect(a, SIGNAL(triggered()), this, SLOT(slotToggleFocus()));

  setComponentData (KComponentData("kate"));
  setXMLFile("plugins/katekonsole/ui.rc");
  m_mw->guiFactory()->addClient (this);

  readConfig();
}

KateConsole::~KateConsole ()
{
  m_mw->guiFactory()->removeClient (this);
  if (m_part)
    disconnect ( m_part, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) );
}

void KateConsole::loadConsoleIfNeeded()
{
  if (m_part) return;

  if (!window() || !parentWidget()) return;
  if (!window() || !isVisibleTo(window())) return;

  KPluginFactory *factory = KPluginLoader("libkonsolepart").factory();

  if (!factory) return;

  m_part = static_cast<KParts::ReadOnlyPart *>(factory->create<QObject>(this, this));

  if (!m_part) return;

  // start the terminal
  qobject_cast<TerminalInterface*>(m_part)->showShellInDir( QString() );

  KGlobal::locale()->insertCatalog("konsole");

  setFocusProxy(m_part->widget());
  m_part->widget()->show();

  connect ( m_part, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) );

  slotSync();
}

void KateConsole::slotDestroyed ()
{
  m_part = 0;

  // hide the dockwidget
  if (parentWidget())
  {
    m_mw->hideToolView (m_toolView);
    m_mw->centralWidget()->setFocus ();
  }
}

void KateConsole::showEvent(QShowEvent *)
{
  if (m_part) return;

  loadConsoleIfNeeded();
}

void KateConsole::cd (const KUrl &url)
{
  sendInput("cd " + KShell::quoteArg(url.path()) + '\n');
}

void KateConsole::sendInput( const QString& text )
{
  loadConsoleIfNeeded();

  if (!m_part) return;

  TerminalInterface *t = qobject_cast<TerminalInterface *>(m_part);

  if (!t) return;

  t->sendInput (text);
}

void KateConsole::slotPipeToConsole ()
{
  if (KMessageBox::warningContinueCancel
      (m_mw->window()
       , i18n ("Do you really want to pipe the text to the console? This will execute any contained commands with your user rights.")
       , i18n ("Pipe to Terminal?")
       , KGuiItem(i18n("Pipe to Terminal")), KStandardGuiItem::cancel(), "Pipe To Terminal Warning") != KMessageBox::Continue)
    return;

  KTextEditor::View *v = m_mw->activeView();

  if (!v)
    return;

  if (v->selection())
    sendInput (v->selectionText());
  else
    sendInput (v->document()->text());
}

void KateConsole::slotSync()
{
  if (m_mw->activeView() ) {
    if ( m_mw->activeView()->document()->url().isValid()
      && m_mw->activeView()->document()->url().isLocalFile() ) {
      cd(KUrl( m_mw->activeView()->document()->url().directory() ));
    } else if ( !m_mw->activeView()->document()->url().isEmpty() ) {
      sendInput( "### " + i18n("Sorry, can not cd into '%1'", m_mw->activeView()->document()->url().directory() ) + "\n" );
    }
  }
}

void KateConsole::slotManualSync()
{
  slotSync();
  if ( ! m_part || ! m_part->widget()->isVisible() )
    m_mw->showToolView( parentWidget() );
}
void KateConsole::slotToggleFocus()
{
  QAction *action = actionCollection()->action("katekonsole_tools_toggle_focus");
  if ( ! m_part ) {
    m_mw->showToolView( parentWidget() );
    action->setText( i18n("Defocus Terminal") );
    return; // this shows and focuses the konsole
  }

  if ( ! m_part ) return;

  if (m_part->widget()->hasFocus()) {
    if (m_mw->activeView())
      m_mw->activeView()->setFocus();
      action->setText( i18n("Focus Terminal") );
  } else {
    // show the view if it is hidden
    if (parentWidget()->isHidden())
      m_mw->showToolView( parentWidget() );
    else // should focus the widget too!
      m_part->widget()->setFocus( Qt::OtherFocusReason );
    action->setText( i18n("Defocus Terminal") );
  }
}

void KateConsole::readConfig()
{
  if ( KConfigGroup(KGlobal::config(), "Konsole").readEntry("AutoSyncronize", false) )
    connect( m_mw, SIGNAL(viewChanged()), SLOT(slotSync()) );
  else
    disconnect( m_mw, SIGNAL(viewChanged()), this, SLOT(slotSync()) );
}

KateKonsoleConfigPage::KateKonsoleConfigPage( QWidget* parent, KateKonsolePlugin *plugin )
  : Kate::PluginConfigPage( parent )
  , mPlugin( plugin )
{
  QVBoxLayout *lo = new QVBoxLayout( this );
  lo->setSpacing( KDialog::spacingHint() );

  cbAutoSyncronize = new QCheckBox( i18n("&Automatically syncronize the terminal with the current document when possible"), this );
  lo->addWidget( cbAutoSyncronize );
  reset();
  lo->addStretch();
  connect( cbAutoSyncronize, SIGNAL(stateChanged(int)), SIGNAL(changed()) );
}

void KateKonsoleConfigPage::apply()
{
  KConfigGroup config(KGlobal::config(), "Konsole");
  config.writeEntry("AutoSyncronize", cbAutoSyncronize->isChecked());
  config.sync();
  mPlugin->readConfig();
}

void KateKonsoleConfigPage::reset()
{
  KConfigGroup config(KGlobal::config(), "Konsole");
  cbAutoSyncronize->setChecked(config.readEntry("AutoSyncronize", false));
}

// kate: space-indent on; indent-width 2; replace-tabs on;

<|endoftext|>


<commit_before>/**
 * Copyright (c) 2008 Will Roberts
 * All Rights Reserved.
 *
 * Name:          count
 *
 * Author:        wildwilhelm@gmail.com (WKR)
 * Creation Date: 11 November 2008
 *
 * Purpose:       Standard input line counter
 *
 * Description:
 *    count counts the number of unique lines on the standard input,
 *    outputting the counts after all the input is read.  The output
 *    of the program can be piped through sort -nr to sort it in order
 *    of descending frequency.
 *
 * Revision Information:
 *
 *    (WKR) 11 November 2008
 *          - Initial version.
 *
 * \file count.cpp
 */

#include <unistd.h>
#include <string>
#include <iostream>
#include <iomanip>
#include <map>
using namespace std;

void
printHelp()
{
    cout << "Counts unique lines on standard input, outputting the counts after all" << endl;
    cout << "input is read.  The output of the program can be piped through sort -nr " << endl;
    cout << "to sort it in order of descending frequency." << endl;
    cout << endl;
    cout << "Syntax:" << endl;
    cout << endl;
    cout << "   count [OPTIONS]" << endl;
    cout << endl;
    cout << "Options:" << endl;
    cout << endl;
    cout << "   -e      always count the last line of the input, even if it is" << endl;
    cout << "           empty" << endl;
    cout << "   -?      display this help message and exit" << endl;
}

int
main ( int    argc,
       char **argv )
{
    bool       fIncludeLastLine = false;
    int        c;
    while ((c = getopt(argc, argv, "e?")) != -1)
    {
        switch(c)
        {
        case 'e':
            fIncludeLastLine = true;
            break;
        case '?':
            printHelp();
            exit(1);
            break;
        default:
            break;
        }
    }

    int             nNumLines = 0;
    map<string,int> LineDict;
    while ( cin.good() )
    {
        string sLine;
        getline ( cin, sLine );
        if (fIncludeLastLine || !cin.eof() || sLine.length() != 0)
        {
            LineDict[sLine] += 1;
        }
        nNumLines       += 1;
    }
    //int nWidth = 0;
    //while ( nNumLines > 0 )
    //{
    //    nWidth++;
    //    nNumLines /= 10;
    //}
    for ( map<string,int>::iterator iterator = LineDict.begin();
          iterator != LineDict.end(); iterator++ )
    {
        cout << iterator->second << "\t" << iterator->first << endl;
    }
    return 0;
}
<commit_msg>count.cpp: add -f option to sort output by frequency<commit_after>/**
 * Copyright (c) 2008 Will Roberts
 * All Rights Reserved.
 *
 * Name:          count
 *
 * Author:        wildwilhelm@gmail.com (WKR)
 * Creation Date: 11 November 2008
 *
 * Purpose:       Standard input line counter
 *
 * Description:
 *    count counts the number of unique lines on the standard input,
 *    outputting the counts after all the input is read.  The output
 *    of the program can be piped through sort -nr to sort it in order
 *    of descending frequency.
 *
 * Revision Information:
 *
 *    (WKR) 11 November 2008
 *          - Initial version.
 *
 * \file count.cpp
 */

#include <unistd.h>
#include <string>
#include <iostream>
#include <iomanip>
#include <map>
using namespace std;

void
printHelp()
{
    cout << "Counts unique lines on standard input, outputting the counts after all" << endl;
    cout << "input is read.  Output is sorted in alphabetical order of lines.  The" << endl;
    cout << "output of the program can be sort it in order of descending frequency" << endl;
    cout << "using the -f option or by piping through sort -nr." << endl;
    cout << endl;
    cout << "Syntax:" << endl;
    cout << endl;
    cout << "   count [OPTIONS]" << endl;
    cout << endl;
    cout << "Options:" << endl;
    cout << endl;
    cout << "   -e      always count the last line of the input, even if it is" << endl;
    cout << "           empty" << endl;
    cout << "           -f      sort output in order of descending frequency" << endl;
    cout << "   -?      display this help message and exit" << endl;
}

template<typename A, typename B>
pair<B,A> flip_pair(const pair<A,B> &p)
{
    return pair<B,A>(p.second, p.first);
}

template<typename A, typename B>
map<B,A> flip_map(const map<A,B> &src)
{
    map<B,A> dst;
    transform(src.begin(), src.end(), inserter(dst, dst.begin()),
              flip_pair<A,B>);
    return dst;
}

int
main ( int    argc,
       char **argv )
{
    bool       fIncludeLastLine    = false;
    bool       fSortDecreasingFreq = false;
    int        c;
    while ((c = getopt(argc, argv, "ef?")) != -1)
    {
        switch(c)
        {
        case 'e':
            fIncludeLastLine = true;
            break;
        case 'f':
            fSortDecreasingFreq = true;
            break;
        case '?':
            printHelp();
            exit(1);
            break;
        default:
            break;
        }
    }

    int             nNumLines = 0;
    map<string,int> LineDict;
    while ( cin.good() )
    {
        string sLine;
        getline ( cin, sLine );
        if (fIncludeLastLine || !cin.eof() || sLine.length() != 0)
        {
            LineDict[sLine] += 1;
        }
        nNumLines       += 1;
    }
    //int nWidth = 0;
    //while ( nNumLines > 0 )
    //{
    //    nWidth++;
    //    nNumLines /= 10;
    //}
    if (fSortDecreasingFreq)
    {
        map<int,string> FlippedLineDict = flip_map(LineDict);
        for ( map<int,string>::reverse_iterator iterator =
                  FlippedLineDict.rbegin();
              iterator != FlippedLineDict.rend(); iterator++ )
        {
            cout << iterator->first << "\t" << iterator->second << endl;
        }
    }
    else
    {
        for ( map<string,int>::iterator iterator = LineDict.begin();
              iterator != LineDict.end(); iterator++ )
        {
            cout << iterator->second << "\t" << iterator->first << endl;
        }
    }
    return 0;
}
<|endoftext|>


<commit_before><commit_msg>Update BCG_LED_Library.cpp<commit_after><|endoftext|>


<commit_before><commit_msg>adjust test for the correct dbdata import<commit_after><|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include <nuttx/config.h>

#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <memory>
#include <pthread.h>
#include <systemlib/err.h>
#include <systemlib/systemlib.h>
#include <systemlib/param/param.h>
#include <systemlib/mixer/mixer.h>
#include <systemlib/board_serial.h>
#include <systemlib/scheduling_priorities.h>
#include <systemlib/git_version.h>
#include <version/version.h>
#include <arch/board/board.h>
#include <arch/chip/chip.h>

#include "uavcan_main.hpp"
#include "uavcan_servers.hpp"
#include "uavcan_virtual_can_driver.hpp"

# include <uavcan_posix/dynamic_node_id_server/file_event_tracer.hpp>
# include <uavcan_posix/dynamic_node_id_server/file_storage_backend.hpp>
# include <uavcan_posix/firmware_version_checker.hpp>

//todo:The Inclusion of file_server_backend is killing
// #include <sys/types.h> and leaving OK undefined
# define OK 0

/**
 * @file uavcan_servers.cpp
 *
 * Implements basic functionality of UAVCAN node.
 *
 * @author Pavel Kirienko <pavel.kirienko@gmail.com>
 *         David Sidrane <david_s5@nscdg.com>
 */

/*
 * UavcanNode
 */
UavcanServers *UavcanServers::_instance;
UavcanServers::UavcanServers(uavcan::INode &main_node) :
	_subnode_thread(-1),
	_vdriver(NumIfaces, uavcan_stm32::SystemClock::instance()),
	_subnode(_vdriver, uavcan_stm32::SystemClock::instance()),
	_main_node(main_node),
	_tracer(),
	_storage_backend(),
	_fw_version_checker(),
	_server_instance(_subnode, _storage_backend, _tracer),
	_fileserver_backend(_subnode),
	_node_info_retriever(_subnode),
	_fw_upgrade_trigger(_subnode, _fw_version_checker),
	_fw_server(_subnode, _fileserver_backend),
	_mutex_inited(false),
	_check_fw(false)

{
}

UavcanServers::~UavcanServers()
{
	if (_mutex_inited) {
		(void)Lock::deinit(_subnode_mutex);
	}
	_main_node.getDispatcher().removeRxFrameListener();
}

int UavcanServers::stop()
{
	UavcanServers *server = instance();

	if (server == nullptr) {
		warnx("Already stopped");
		return -1;
	}

	_instance = nullptr;

	if (server->_subnode_thread != -1) {
		pthread_cancel(server->_subnode_thread);
		pthread_join(server->_subnode_thread, NULL);
	}

	server->_main_node.getDispatcher().removeRxFrameListener();

	delete server;
	return 0;
}

int UavcanServers::start(uavcan::INode &main_node)
{
	if (_instance != nullptr) {
		warnx("Already started");
		return -1;
	}

	/*
	 * Node init
	 */
	_instance = new UavcanServers(main_node);

	if (_instance == nullptr) {
		warnx("Out of memory");
		return -2;
	}

	int rv  = _instance->init();

	if (rv < 0) {
		warnx("Node init failed: %d", rv);
		delete _instance;
		_instance = nullptr;
		return rv;
	}

	/*
	 * Start the thread. Normally it should never exit.
	 */
	pthread_attr_t tattr;
	struct sched_param param;

	pthread_attr_init(&tattr);
	tattr.stacksize = StackSize;
	param.sched_priority = Priority;
	pthread_attr_setschedparam(&tattr, &param);

	static auto run_trampoline = [](void *) {return UavcanServers::_instance->run(_instance);};

	rv = pthread_create(&_instance->_subnode_thread, &tattr, static_cast<pthread_startroutine_t>(run_trampoline), NULL);

	if (rv < 0) {
		warnx("pthread_create() failed: %d", errno);
		rv =  -errno;
		delete _instance;
		_instance = nullptr;
	}

	return rv;
}

int UavcanServers::init()
{
	errno = 0;

	/*
	 * Initialize the mutex.
	 * giving it its path
	 */

	int ret = Lock::init(_subnode_mutex);

	if (ret < 0) {
		warnx("Lock init: %d", errno);
		return ret;
	}

	_mutex_inited = true;

	_subnode.setNodeID(_main_node.getNodeID());
	_main_node.getDispatcher().installRxFrameListener(&_vdriver);

	/*
	 * Initialize the fw version checker.
	 * giving it its path
	 */
	ret = _fw_version_checker.createFwPaths(UAVCAN_FIRMWARE_PATH);

	if (ret < 0) {
		warnx("FirmwareVersionChecker init: %d, errno: %d", ret, errno);
		return ret;
	}

	/* Start fw file server back */

	ret = _fw_server.start();

	if (ret < 0) {
		warnx("BasicFileServer init: %d, errno: %d", ret, errno);
		return ret;
	}

	/* Initialize storage back end for the node allocator using UAVCAN_NODE_DB_PATH directory */

	ret = _storage_backend.init(UAVCAN_NODE_DB_PATH);

	if (ret < 0) {
		warnx("FileStorageBackend init: %d, errno: %d", ret, errno);
		return ret;
	}

	/* Initialize trace in the UAVCAN_NODE_DB_PATH directory */

	ret = _tracer.init(UAVCAN_LOG_FILE);

	if (ret < 0) {
		warnx("FileEventTracer init: %d, errno: %d", ret, errno);
		return ret;
	}

	/* hardware version */
	uavcan::protocol::HardwareVersion hwver;
	UavcanNode::getHardwareVersion(hwver);

	/* Initialize the dynamic node id server  */
	ret = _server_instance.init(hwver.unique_id);

	if (ret < 0) {
		warnx("CentralizedServer init: %d", ret);
		return ret;
	}

	/* Start node info retriever to fetch node info from new nodes */

	ret = _node_info_retriever.start();

	if (ret < 0) {
		warnx("NodeInfoRetriever init: %d", ret);
		return ret;
	}

	/* Start the fw version checker   */

	ret = _fw_upgrade_trigger.start(_node_info_retriever, _fw_version_checker.getFirmwarePath());

	if (ret < 0) {
		warnx("FirmwareUpdateTrigger init: %d", ret);
		return ret;
	}

	/*  Start the Node   */

	return OK;
}

__attribute__((optimize("-O0")))
pthread_addr_t UavcanServers::run(pthread_addr_t)
{
	Lock lock(_subnode_mutex);

	while (1) {

		if (_check_fw == true) {
			_check_fw = false;
			_node_info_retriever.invalidateAll();
		}

		const int spin_res = _subnode.spin(uavcan::MonotonicDuration::fromMSec(100));

		if (spin_res < 0) {
			warnx("node spin error %i", spin_res);
		}
	}

	warnx("exiting.");
	return (pthread_addr_t) 0;
}

<commit_msg>Bugfix:pthread_create retuns 0 on success and positive error on error without errono set<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include <nuttx/config.h>

#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <memory>
#include <pthread.h>
#include <systemlib/err.h>
#include <systemlib/systemlib.h>
#include <systemlib/param/param.h>
#include <systemlib/mixer/mixer.h>
#include <systemlib/board_serial.h>
#include <systemlib/scheduling_priorities.h>
#include <systemlib/git_version.h>
#include <version/version.h>
#include <arch/board/board.h>
#include <arch/chip/chip.h>

#include "uavcan_main.hpp"
#include "uavcan_servers.hpp"
#include "uavcan_virtual_can_driver.hpp"

# include <uavcan_posix/dynamic_node_id_server/file_event_tracer.hpp>
# include <uavcan_posix/dynamic_node_id_server/file_storage_backend.hpp>
# include <uavcan_posix/firmware_version_checker.hpp>

//todo:The Inclusion of file_server_backend is killing
// #include <sys/types.h> and leaving OK undefined
# define OK 0

/**
 * @file uavcan_servers.cpp
 *
 * Implements basic functionality of UAVCAN node.
 *
 * @author Pavel Kirienko <pavel.kirienko@gmail.com>
 *         David Sidrane <david_s5@nscdg.com>
 */

/*
 * UavcanNode
 */
UavcanServers *UavcanServers::_instance;
UavcanServers::UavcanServers(uavcan::INode &main_node) :
	_subnode_thread(-1),
	_vdriver(NumIfaces, uavcan_stm32::SystemClock::instance()),
	_subnode(_vdriver, uavcan_stm32::SystemClock::instance()),
	_main_node(main_node),
	_tracer(),
	_storage_backend(),
	_fw_version_checker(),
	_server_instance(_subnode, _storage_backend, _tracer),
	_fileserver_backend(_subnode),
	_node_info_retriever(_subnode),
	_fw_upgrade_trigger(_subnode, _fw_version_checker),
	_fw_server(_subnode, _fileserver_backend),
	_mutex_inited(false),
	_check_fw(false)

{
}

UavcanServers::~UavcanServers()
{
	if (_mutex_inited) {
		(void)Lock::deinit(_subnode_mutex);
	}

	_main_node.getDispatcher().removeRxFrameListener();
}

int UavcanServers::stop()
{
	UavcanServers *server = instance();

	if (server == nullptr) {
		warnx("Already stopped");
		return -1;
	}

	_instance = nullptr;

	if (server->_subnode_thread != -1) {
		pthread_cancel(server->_subnode_thread);
		pthread_join(server->_subnode_thread, NULL);
	}

	server->_main_node.getDispatcher().removeRxFrameListener();

	delete server;
	return 0;
}

int UavcanServers::start(uavcan::INode &main_node)
{
	if (_instance != nullptr) {
		warnx("Already started");
		return -1;
	}

	/*
	 * Node init
	 */
	_instance = new UavcanServers(main_node);

	if (_instance == nullptr) {
		warnx("Out of memory");
		return -2;
	}

	int rv  = _instance->init();

	if (rv < 0) {
		warnx("Node init failed: %d", rv);
		delete _instance;
		_instance = nullptr;
		return rv;
	}

	/*
	 * Start the thread. Normally it should never exit.
	 */
	pthread_attr_t tattr;
	struct sched_param param;

	pthread_attr_init(&tattr);
	tattr.stacksize = StackSize;
	param.sched_priority = Priority;
	pthread_attr_setschedparam(&tattr, &param);

	static auto run_trampoline = [](void *) {return UavcanServers::_instance->run(_instance);};

	rv = pthread_create(&_instance->_subnode_thread, &tattr, static_cast<pthread_startroutine_t>(run_trampoline), NULL);

	if (rv != 0) {
		rv = -rv;
		warnx("pthread_create() failed: %d", rv);
		delete _instance;
		_instance = nullptr;
	}

	return rv;
}

int UavcanServers::init()
{
	errno = 0;

	/*
	 * Initialize the mutex.
	 * giving it its path
	 */

	int ret = Lock::init(_subnode_mutex);

	if (ret < 0) {
		warnx("Lock init: %d", errno);
		return ret;
	}

	_mutex_inited = true;

	_subnode.setNodeID(_main_node.getNodeID());
	_main_node.getDispatcher().installRxFrameListener(&_vdriver);

	/*
	 * Initialize the fw version checker.
	 * giving it its path
	 */
	ret = _fw_version_checker.createFwPaths(UAVCAN_FIRMWARE_PATH);

	if (ret < 0) {
		warnx("FirmwareVersionChecker init: %d, errno: %d", ret, errno);
		return ret;
	}

	/* Start fw file server back */

	ret = _fw_server.start();

	if (ret < 0) {
		warnx("BasicFileServer init: %d, errno: %d", ret, errno);
		return ret;
	}

	/* Initialize storage back end for the node allocator using UAVCAN_NODE_DB_PATH directory */

	ret = _storage_backend.init(UAVCAN_NODE_DB_PATH);

	if (ret < 0) {
		warnx("FileStorageBackend init: %d, errno: %d", ret, errno);
		return ret;
	}

	/* Initialize trace in the UAVCAN_NODE_DB_PATH directory */

	ret = _tracer.init(UAVCAN_LOG_FILE);

	if (ret < 0) {
		warnx("FileEventTracer init: %d, errno: %d", ret, errno);
		return ret;
	}

	/* hardware version */
	uavcan::protocol::HardwareVersion hwver;
	UavcanNode::getHardwareVersion(hwver);

	/* Initialize the dynamic node id server  */
	ret = _server_instance.init(hwver.unique_id);

	if (ret < 0) {
		warnx("CentralizedServer init: %d", ret);
		return ret;
	}

	/* Start node info retriever to fetch node info from new nodes */

	ret = _node_info_retriever.start();

	if (ret < 0) {
		warnx("NodeInfoRetriever init: %d", ret);
		return ret;
	}

	/* Start the fw version checker   */

	ret = _fw_upgrade_trigger.start(_node_info_retriever, _fw_version_checker.getFirmwarePath());

	if (ret < 0) {
		warnx("FirmwareUpdateTrigger init: %d", ret);
		return ret;
	}

	/*  Start the Node   */

	return OK;
}

__attribute__((optimize("-O0")))
pthread_addr_t UavcanServers::run(pthread_addr_t)
{
	Lock lock(_subnode_mutex);

	while (1) {

		if (_check_fw == true) {
			_check_fw = false;
			_node_info_retriever.invalidateAll();
		}

		const int spin_res = _subnode.spin(uavcan::MonotonicDuration::fromMSec(100));

		if (spin_res < 0) {
			warnx("node spin error %i", spin_res);
		}
	}

	warnx("exiting.");
	return (pthread_addr_t) 0;
}

<|endoftext|>


<commit_before>/// @file
/// @author uentity
/// @date 23.04.2019
/// @brief BS kernel Python subsystem impl
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/python/common.h>
#include <bs/log.h>
#include <bs/kernel/errors.h>
#include <bs/tree/node.h>

#include "kimpl.h"
#include "python_subsyst_impl.h"
#include "plugins_subsyst.h"
#include "radio_subsyst.h"

#include <pybind11/functional.h>

#include <iostream>
#include <functional>

#define WITH_KMOD \
[[maybe_unused]] auto res = pykmod(this).map([&](auto kmod_ptr) { \
[[maybe_unused]] auto& kmod = *kmod_ptr;

#define END_WITH });

#define RETURN_WITH END_WITH return res.value();

#define RETURN_WITH_FAIL(err_val) END_WITH return res.value_or(err_val);

NAMESPACE_BEGIN(blue_sky::kernel::detail)
using namespace blue_sky::detail;
namespace py = pybind11;

NAMESPACE_BEGIN()

auto pykmod(const python_subsyst* v) -> result_or_err<pybind11::module*> {
	if(v) if(auto kmod = v->py_kmod())
		return { static_cast<pybind11::module*>(kmod) };
	return tl::unexpected{Error::BadPymod};
}

std::string extract_root_name(const std::string& full_name) {
	using namespace std;

	//extract user-friendly lib name from full name
	string pyl_name = full_name;
	//cut path
	string::size_type pos = pyl_name.rfind('/');
	if(pos != string::npos) pyl_name = pyl_name.substr(pos + 1, string::npos);
	pos = pyl_name.rfind('\\');
	if(pos != string::npos) pyl_name = pyl_name.substr(pos + 1, string::npos);
	//cut extension
	pyl_name = pyl_name.substr(0, pyl_name.find('.'));
	//cut "lib" prefix if exists
	if(pyl_name.compare(0, 3, string("lib")) == 0)
		pyl_name = pyl_name.substr(3, string::npos);

	return pyl_name;
}

NAMESPACE_END()

/*-----------------------------------------------------------------------------
 *  python_subsyst_impl
 *-----------------------------------------------------------------------------*/
python_subsyst_impl::python_subsyst_impl(void* kmod_ptr) :
	kmod_(nullptr)
{
	setup_py_kmod(kmod_ptr);
}

python_subsyst_impl::~python_subsyst_impl() {}

auto python_subsyst_impl::self() -> python_subsyst_impl& {
	// [NOTE] unconditional cast, because if self() is called, then instance MUST be self for sure
	return static_cast<python_subsyst_impl&>(singleton<python_subsyst>::Instance());
}

auto python_subsyst_impl::py_kmod() const -> void* { return kmod_; }

auto python_subsyst_impl::setup_py_kmod(void* kmod_ptr) -> void {
	if(!kmod_ptr) return;

	kmod_ = kmod_ptr;
	auto& kmod = *static_cast<pybind11::module*>(kmod_);

	auto& kpd = plugins_subsyst::kernel_pd();
	kmod.doc() = kpd.description;
	kpd.py_namespace = PyModule_GetName(kmod.ptr());

	// [IMPORTANT] we must clean up adapters & cache right BEFORE interpreter termination
	auto at_pyexit = py::module::import("atexit");
	at_pyexit.attr("register")(py::cpp_function{[this] {
		std::cout << "~~~ Python subsystem shutting down..." << std::endl;
		drop_adapted_cache();
		adapters_.clear();
		def_adapter_ = nullptr;

		// kick all event-based actors (normally wait until all actor handles wired into Python are released)
		{
			auto _ = py::gil_scoped_release{};
			const auto kradio = KIMPL.get_radio();
			kradio->kick_citizens();
			// stop queue while Python is still alive to gracefully finish all pending transactions
			kradio->stop_queue(true);
		}

		// force garbage collection
		auto gc = py::module::import("gc");
		gc.attr("collect")();
		gc.attr("collect")();

		std::cout << "~~~ Python subsystem down" << std::endl;
	}});
}

auto python_subsyst_impl::py_init_plugin(
	const blue_sky::detail::lib_descriptor& lib, plugin_descriptor& p_descr
) -> result_or_err<std::string> {
	return pykmod(this).and_then([&](auto kmod_ptr) -> result_or_err<std::string> {
		bs_init_py_fn init_py_fn;
		lib.load_sym("bs_init_py_subsystem", init_py_fn);
		if(!init_py_fn) return tl::make_unexpected(error{p_descr.name, kernel::Error::PythonDisabled});

		if(p_descr.py_namespace.empty())
			p_descr.py_namespace = extract_root_name(lib.fname_);

		// create submodule
		auto plugin_mod = kmod_ptr->def_submodule(p_descr.py_namespace.c_str(), p_descr.description.c_str());
		// invoke init function
		init_py_fn(&plugin_mod);

		return plugins_subsyst::kernel_pd().py_namespace + '.' + p_descr.py_namespace;
	});
}

auto python_subsyst_impl::py_add_error_closure() -> void {
WITH_KMOD
	auto err_class = (pybind11::class_<std::error_code>)kmod.attr("error_code");
	pybind11::init([](int ec) {
		return std::error_code(static_cast<Error>(ec));
	}).execute(err_class);
END_WITH
}

auto python_subsyst_impl::register_adapter(std::string obj_type_id, adapter_fn f) -> void {
	if(f)
		adapters_.insert_or_assign(std::move(obj_type_id), std::move(f));
	else {
		auto pa = adapters_.find(obj_type_id);
		if(pa != adapters_.end())
			adapters_.erase(pa);
	}
}

auto python_subsyst_impl::register_default_adapter(adapter_fn f) -> void {
	def_adapter_ = std::move(f);
}

auto python_subsyst_impl::clear_adapters() -> void {
	drop_adapted_cache();
	adapters_.clear();
	def_adapter_ = nullptr;
}

auto python_subsyst_impl::adapted_types() const -> std::vector<std::string> {
	std::vector<std::string> res(adapters_.size());
	std::transform(
		adapters_.begin(), adapters_.end(), res.begin(),
		[](const auto& A){ return A.first; }
	);
	if(def_adapter_) res.push_back("*");
	return res;
}

auto python_subsyst_impl::adapt(sp_obj source, const tree::link& L) -> py::object {
	if(!source) return py::none();

	// register link and return whether link is met for the first time
	const auto remember_link = [&](auto* data_ptr) {
		if(!lnk2obj_.try_emplace(L.id(), data_ptr).second) return false;
		// erase adapter on link delete
		L.subscribe([](const auto& ev) {
			const auto& [_, params, __] = ev;
			const auto* lid = prop::get_if<uuid>(&params, "link_id");
			if(!lid) return;

			auto& self = python_subsyst_impl::self();
			auto cached_L = self.lnk2obj_.find(*lid);
			if(cached_L == self.lnk2obj_.end()) return;

			// kill cache entry if ref counter reaches zero
			if(auto cached_A = self.acache_.find(cached_L->second); cached_A != self.acache_.end()) {
				if(--cached_A->second.second == 0) {
					auto py_guard = py::gil_scoped_acquire();
					self.acache_.erase(cached_A);
				}
			}
			self.lnk2obj_.erase(cached_L);
		}, tree::Event::LinkDeleted);
		return true;
	};

	// check if adapter already created for given object
	auto* data_ptr = source.get();
	if(auto cached_A = acache_.find(data_ptr); cached_A != acache_.end()) {
		// inc ref counter for new link
		auto& [pyobj, use_count] = cached_A->second;
		if(remember_link(data_ptr))
			++use_count;
		return pyobj;
	}

	// adapt or passthrough
	const auto adapt_and_cache = [&](auto&& afn) {
		// cache adapter with ref counter = 1
		return acache_.try_emplace(
			data_ptr, afn(std::move(source)), size_t{ remember_link(data_ptr) }
		).first->second.first;
	};

	auto pf = adapters_.find(source->type_id());
	return pf != adapters_.end() ?
		adapt_and_cache(pf->second) :
		( def_adapter_ ? adapt_and_cache(def_adapter_) : py::cast(source) );
}

auto python_subsyst_impl::get_cached_adapter(const sp_obj& obj) const -> pybind11::object {
	if(auto cached_A = acache_.find(obj.get()); cached_A != acache_.end())
		return cached_A->second.first;
	return py::none();
}

auto python_subsyst_impl::drop_adapted_cache(const sp_obj& obj) -> std::size_t {
	std::size_t res = 0;
	if(!obj) {
		res = acache_.size();
		acache_.clear();
		lnk2obj_.clear();
	}
	else if(auto cached_A = acache_.find(obj.get()); cached_A != acache_.end()) {
		// clean link -> obj ptr resolver
		auto* obj_ptr = cached_A->first;
		for(auto p_lnk = lnk2obj_.begin(); p_lnk != lnk2obj_.end();) {
			if(p_lnk->second == obj_ptr)
				p_lnk = lnk2obj_.erase(p_lnk);
			else
				++p_lnk;
		}
		// clean cached adapter
		acache_.erase(cached_A);
		res = 1;
	}
	return res;
}

NAMESPACE_END(blue_sky::kernel::detail)

NAMESPACE_BEGIN(blue_sky::python)

auto py_bind_adapters(py::module& m) -> void {
	using namespace kernel::detail;
	static auto py_kernel = &python_subsyst_impl::self;

	// export adapters manip functions
	using adapter_fn = kernel::detail::python_subsyst_impl::adapter_fn;

	m.def("register_adapter", [](std::string obj_type_id, adapter_fn f) {
			py_kernel().register_adapter(std::move(obj_type_id), std::move(f));
		}, "obj_type_id"_a, "adapter_fn"_a, "Register adapter for specified BS type"
	);
	m.def("register_default_adapter", [](adapter_fn f) {
			py_kernel().register_default_adapter(std::move(f));
		}, "adapter_fn"_a, "Register default adapter for all BS types with no adapter registered"
	);
	m.def("adapted_types", []() { return py_kernel().adapted_types(); },
		"Return list of types with registered adapters ('*' denotes default adapter)"
	);
	m.def("adapt", [](sp_obj source, const tree::link& L) {
			return py_kernel().adapt(std::move(source), L);
		}, "source"_a, "lnk"_a,
		"Make adapter for given object"
	);

	m.def("clear_adapters", []() { py_kernel().clear_adapters(); },
		"Remove all adapters (including default) for BS types"
	);
	m.def("drop_adapted_cache", [](const sp_obj& obj) {
			return py_kernel().drop_adapted_cache(obj);
		}, "obj"_a = nullptr,
		"Clear cached adapter for given object (or drop all cached adapters if object is None)"
	);
	m.def("get_cached_adapter", [](const sp_obj& obj) {
			return py_kernel().get_cached_adapter(obj);
		},
		"obj"_a, "Get cached adapter for given object (if created before, otherwise None)"
	);

	m.def("enqueue", [](py::function f) {
		KRADIO.enqueue(launch_async, [f = std::move(f)] {
			f();
			return perfect;
		});
	}, "f"_a, "Async run Python function in BS queue");
}

NAMESPACE_END(blue_sky::python)
<commit_msg>kernel/python: run adapter cleanup after link deletion in kernel's queue<commit_after>/// @file
/// @author uentity
/// @date 23.04.2019
/// @brief BS kernel Python subsystem impl
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/python/common.h>
#include <bs/log.h>
#include <bs/kernel/errors.h>
#include <bs/tree/node.h>

#include "kimpl.h"
#include "python_subsyst_impl.h"
#include "plugins_subsyst.h"
#include "radio_subsyst.h"

#include <pybind11/functional.h>

#include <iostream>
#include <functional>

#define WITH_KMOD \
[[maybe_unused]] auto res = pykmod(this).map([&](auto kmod_ptr) { \
[[maybe_unused]] auto& kmod = *kmod_ptr;

#define END_WITH });

#define RETURN_WITH END_WITH return res.value();

#define RETURN_WITH_FAIL(err_val) END_WITH return res.value_or(err_val);

NAMESPACE_BEGIN(blue_sky::kernel::detail)
using namespace blue_sky::detail;
namespace py = pybind11;

NAMESPACE_BEGIN()

auto pykmod(const python_subsyst* v) -> result_or_err<pybind11::module*> {
	if(v) if(auto kmod = v->py_kmod())
		return { static_cast<pybind11::module*>(kmod) };
	return tl::unexpected{Error::BadPymod};
}

std::string extract_root_name(const std::string& full_name) {
	using namespace std;

	//extract user-friendly lib name from full name
	string pyl_name = full_name;
	//cut path
	string::size_type pos = pyl_name.rfind('/');
	if(pos != string::npos) pyl_name = pyl_name.substr(pos + 1, string::npos);
	pos = pyl_name.rfind('\\');
	if(pos != string::npos) pyl_name = pyl_name.substr(pos + 1, string::npos);
	//cut extension
	pyl_name = pyl_name.substr(0, pyl_name.find('.'));
	//cut "lib" prefix if exists
	if(pyl_name.compare(0, 3, string("lib")) == 0)
		pyl_name = pyl_name.substr(3, string::npos);

	return pyl_name;
}

NAMESPACE_END()

/*-----------------------------------------------------------------------------
 *  python_subsyst_impl
 *-----------------------------------------------------------------------------*/
python_subsyst_impl::python_subsyst_impl(void* kmod_ptr) :
	kmod_(nullptr)
{
	setup_py_kmod(kmod_ptr);
}

python_subsyst_impl::~python_subsyst_impl() {}

auto python_subsyst_impl::self() -> python_subsyst_impl& {
	// [NOTE] unconditional cast, because if self() is called, then instance MUST be self for sure
	return static_cast<python_subsyst_impl&>(singleton<python_subsyst>::Instance());
}

auto python_subsyst_impl::py_kmod() const -> void* { return kmod_; }

auto python_subsyst_impl::setup_py_kmod(void* kmod_ptr) -> void {
	if(!kmod_ptr) return;

	kmod_ = kmod_ptr;
	auto& kmod = *static_cast<pybind11::module*>(kmod_);

	auto& kpd = plugins_subsyst::kernel_pd();
	kmod.doc() = kpd.description;
	kpd.py_namespace = PyModule_GetName(kmod.ptr());

	// [IMPORTANT] we must clean up adapters & cache right BEFORE interpreter termination
	auto at_pyexit = py::module::import("atexit");
	at_pyexit.attr("register")(py::cpp_function{[this] {
		std::cout << "~~~ Python subsystem shutting down..." << std::endl;
		drop_adapted_cache();
		adapters_.clear();
		def_adapter_ = nullptr;

		// kick all event-based actors (normally wait until all actor handles wired into Python are released)
		{
			auto _ = py::gil_scoped_release{};
			const auto kradio = KIMPL.get_radio();
			kradio->kick_citizens();
			// stop queue while Python is still alive to gracefully finish all pending transactions
			kradio->stop_queue(true);
		}

		// force garbage collection
		auto gc = py::module::import("gc");
		gc.attr("collect")();
		gc.attr("collect")();

		std::cout << "~~~ Python subsystem down" << std::endl;
	}});
}

auto python_subsyst_impl::py_init_plugin(
	const blue_sky::detail::lib_descriptor& lib, plugin_descriptor& p_descr
) -> result_or_err<std::string> {
	return pykmod(this).and_then([&](auto kmod_ptr) -> result_or_err<std::string> {
		bs_init_py_fn init_py_fn;
		lib.load_sym("bs_init_py_subsystem", init_py_fn);
		if(!init_py_fn) return tl::make_unexpected(error{p_descr.name, kernel::Error::PythonDisabled});

		if(p_descr.py_namespace.empty())
			p_descr.py_namespace = extract_root_name(lib.fname_);

		// create submodule
		auto plugin_mod = kmod_ptr->def_submodule(p_descr.py_namespace.c_str(), p_descr.description.c_str());
		// invoke init function
		init_py_fn(&plugin_mod);

		return plugins_subsyst::kernel_pd().py_namespace + '.' + p_descr.py_namespace;
	});
}

auto python_subsyst_impl::py_add_error_closure() -> void {
WITH_KMOD
	auto err_class = (pybind11::class_<std::error_code>)kmod.attr("error_code");
	pybind11::init([](int ec) {
		return std::error_code(static_cast<Error>(ec));
	}).execute(err_class);
END_WITH
}

auto python_subsyst_impl::register_adapter(std::string obj_type_id, adapter_fn f) -> void {
	if(f)
		adapters_.insert_or_assign(std::move(obj_type_id), std::move(f));
	else {
		auto pa = adapters_.find(obj_type_id);
		if(pa != adapters_.end())
			adapters_.erase(pa);
	}
}

auto python_subsyst_impl::register_default_adapter(adapter_fn f) -> void {
	def_adapter_ = std::move(f);
}

auto python_subsyst_impl::clear_adapters() -> void {
	drop_adapted_cache();
	adapters_.clear();
	def_adapter_ = nullptr;
}

auto python_subsyst_impl::adapted_types() const -> std::vector<std::string> {
	std::vector<std::string> res(adapters_.size());
	std::transform(
		adapters_.begin(), adapters_.end(), res.begin(),
		[](const auto& A){ return A.first; }
	);
	if(def_adapter_) res.push_back("*");
	return res;
}

auto python_subsyst_impl::adapt(sp_obj source, const tree::link& L) -> py::object {
	if(!source) return py::none();

	// register link and return whether link is met for the first time
	const auto remember_link = [&](auto* data_ptr) {
		if(!lnk2obj_.try_emplace(L.id(), data_ptr).second) return false;
		// erase adapter on link delete
		L.subscribe([](const auto& ev) {
			const auto& [_, params, __] = ev;
			const auto* lid = prop::get_if<uuid>(&params, "link_id");
			if(!lid) return;

			KRADIO.enqueue(launch_async, [lid = *lid] {
				auto py_guard = py::gil_scoped_acquire();
				auto& self = python_subsyst_impl::self();
				auto cached_L = self.lnk2obj_.find(lid);
				if(cached_L == self.lnk2obj_.end()) return perfect;

				// kill cache entry if ref counter reaches zero
				if(auto cached_A = self.acache_.find(cached_L->second); cached_A != self.acache_.end()) {
					if(--cached_A->second.second == 0)
						self.acache_.erase(cached_A);
				}
				self.lnk2obj_.erase(cached_L);
				return perfect;
			});
		}, tree::Event::LinkDeleted);
		return true;
	};

	// check if adapter already created for given object
	auto* data_ptr = source.get();
	if(auto cached_A = acache_.find(data_ptr); cached_A != acache_.end()) {
		// inc ref counter for new link
		auto& [pyobj, use_count] = cached_A->second;
		if(remember_link(data_ptr))
			++use_count;
		return pyobj;
	}

	// adapt or passthrough
	const auto adapt_and_cache = [&](auto&& afn) {
		// cache adapter with ref counter = 1
		return acache_.try_emplace(
			data_ptr, afn(std::move(source)), size_t{ remember_link(data_ptr) }
		).first->second.first;
	};

	auto pf = adapters_.find(source->type_id());
	return pf != adapters_.end() ?
		adapt_and_cache(pf->second) :
		( def_adapter_ ? adapt_and_cache(def_adapter_) : py::cast(source) );
}

auto python_subsyst_impl::get_cached_adapter(const sp_obj& obj) const -> pybind11::object {
	if(auto cached_A = acache_.find(obj.get()); cached_A != acache_.end())
		return cached_A->second.first;
	return py::none();
}

auto python_subsyst_impl::drop_adapted_cache(const sp_obj& obj) -> std::size_t {
	std::size_t res = 0;
	if(!obj) {
		res = acache_.size();
		acache_.clear();
		lnk2obj_.clear();
	}
	else if(auto cached_A = acache_.find(obj.get()); cached_A != acache_.end()) {
		// clean link -> obj ptr resolver
		auto* obj_ptr = cached_A->first;
		for(auto p_lnk = lnk2obj_.begin(); p_lnk != lnk2obj_.end();) {
			if(p_lnk->second == obj_ptr)
				p_lnk = lnk2obj_.erase(p_lnk);
			else
				++p_lnk;
		}
		// clean cached adapter
		acache_.erase(cached_A);
		res = 1;
	}
	return res;
}

NAMESPACE_END(blue_sky::kernel::detail)

NAMESPACE_BEGIN(blue_sky::python)

auto py_bind_adapters(py::module& m) -> void {
	using namespace kernel::detail;
	static auto py_kernel = &python_subsyst_impl::self;

	// export adapters manip functions
	using adapter_fn = kernel::detail::python_subsyst_impl::adapter_fn;

	m.def("register_adapter", [](std::string obj_type_id, adapter_fn f) {
			py_kernel().register_adapter(std::move(obj_type_id), std::move(f));
		}, "obj_type_id"_a, "adapter_fn"_a, "Register adapter for specified BS type"
	);
	m.def("register_default_adapter", [](adapter_fn f) {
			py_kernel().register_default_adapter(std::move(f));
		}, "adapter_fn"_a, "Register default adapter for all BS types with no adapter registered"
	);
	m.def("adapted_types", []() { return py_kernel().adapted_types(); },
		"Return list of types with registered adapters ('*' denotes default adapter)"
	);
	m.def("adapt", [](sp_obj source, const tree::link& L) {
			return py_kernel().adapt(std::move(source), L);
		}, "source"_a, "lnk"_a,
		"Make adapter for given object"
	);

	m.def("clear_adapters", []() { py_kernel().clear_adapters(); },
		"Remove all adapters (including default) for BS types"
	);
	m.def("drop_adapted_cache", [](const sp_obj& obj) {
			return py_kernel().drop_adapted_cache(obj);
		}, "obj"_a = nullptr,
		"Clear cached adapter for given object (or drop all cached adapters if object is None)"
	);
	m.def("get_cached_adapter", [](const sp_obj& obj) {
			return py_kernel().get_cached_adapter(obj);
		},
		"obj"_a, "Get cached adapter for given object (if created before, otherwise None)"
	);

	m.def("enqueue", [](py::function f) {
		KRADIO.enqueue(launch_async, [f = std::move(f)] {
			f();
			return perfect;
		});
	}, "f"_a, "Async run Python function in BS queue");
}

NAMESPACE_END(blue_sky::python)
<|endoftext|>


<commit_before>// -----------------------------------------------------------------------
// pion-common: a collection of common libraries used by the Pion Platform
// -----------------------------------------------------------------------
// Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
//

#ifndef __PION_PIONLOCKEDQUEUE_HEADER__
#define __PION_PIONLOCKEDQUEUE_HEADER__

#include <new>
#include <boost/cstdint.hpp>
#include <boost/noncopyable.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/detail/atomic_count.hpp>
#include <pion/PionConfig.hpp>
#include <pion/PionException.hpp>
#if defined(PION_HAVE_LOCKFREE) && !defined(_MSC_VER)
	#include <boost/lockfree/freelist.hpp>
#endif

// NOTE: the data structures contained in this file are based upon algorithms
// published in the paper "Simple, Fast, and Practical Non-Blocking and Blocking
// Concurrent Queue Algorithms" (1996, Maged M. Michael and Michael L. Scott,
// Department of Computer Science, University of Rochester).
// See http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf

namespace pion {	// begin namespace pion

///
/// PionLockedQueue: a thread-safe, two-lock concurrent FIFO queue
///
template <typename T,
	boost::uint32_t MaxSize = 250000,
	boost::uint32_t SleepMilliSec = 10 >
class PionLockedQueue :
	private boost::noncopyable
{
protected:

	/// data structure used to wrap each item in the queue
	struct QueueNode {
		T		data;		//< data wrapped by the node item
		QueueNode *	next;		//< points to the next node in the queue
		boost::uint32_t	version;	//< the node item's version number
	};

	/// returns a new queue node item for use in the queue
	inline QueueNode *createNode(void) {
#if defined(PION_HAVE_LOCKFREE) && !defined(_MSC_VER)
		return new (m_free_list.allocate()) QueueNode();
#else
		return new QueueNode();
#endif
	}

	/// frees memory for an existing queue node item
	inline void destroyNode(QueueNode *node_ptr) {
#if defined(PION_HAVE_LOCKFREE) && !defined(_MSC_VER)
		node_ptr->~QueueNode();
		m_free_list.deallocate(node_ptr);
#else
		delete node_ptr;
#endif
	}

	/**
	 * dequeues the next item from the top of the queue
	 *
	 * @param t assigned to the item at the top of the queue, if it is not empty
	 *
	 * @return boost::uint32_t version number of the item retrieved; zero if none
	 */
	inline boost::uint32_t dequeue(T& t) {
		// just return if the list is empty
		boost::mutex::scoped_lock head_lock(m_head_mutex);
		QueueNode *new_head_ptr = m_head_ptr->next;
		if (! new_head_ptr)
			return 0;

		// get a copy of the item at the head of the list
		boost::uint32_t version = new_head_ptr->version;
		t = new_head_ptr->data;

		// update the pointer to the head of the list
		QueueNode *old_head_ptr = m_head_ptr;
		m_head_ptr = new_head_ptr;
		head_lock.unlock();

		// free the QueueNode for the old head of the list
		destroyNode(old_head_ptr);

		// only track size if MaxSize > 0
		if (MaxSize > 0)
			--m_size;

		// item successfully dequeued
		return version;
	}

public:

	/// data structure used to manage idle thread signaling
	struct IdleThreadInfo {
		boost::condition	wakeup_event;	//< triggered when a new item is available
		IdleThreadInfo *	next;		//< pointer to the next idle thread
	};

	/// constructs a new PionLockedQueue
	PionLockedQueue(void)
		: m_head_ptr(NULL), m_tail_ptr(NULL), m_idle_ptr(NULL),
		m_next_version(1), m_size(0)
	{
		// initialize with a dummy node since m_head_ptr is always
		// pointing to the item before the head of the list
		m_head_ptr = m_tail_ptr = createNode();
		m_head_ptr->next = NULL;
		m_head_ptr->version = 0;
	}

	/// virtual destructor
	virtual ~PionLockedQueue() {
		clear();
		destroyNode(m_tail_ptr);
	}

	/// returns true if the queue is empty; false if it is not
	inline bool empty(void) const { return (m_head_ptr->next == NULL); }

	/// returns the number of items that are currently in the queue
	std::size_t size(void) const {
		// only enable size() if MaxSize > 0
		PION_ASSERT(MaxSize > 0);
		return m_size;
	}

	/// clears the list by removing all remaining items
	void clear(void) {
		boost::mutex::scoped_lock tail_lock(m_tail_mutex);
		boost::mutex::scoped_lock head_lock(m_head_mutex);
		while (m_head_ptr->next) {
			m_tail_ptr = m_head_ptr;
			m_head_ptr = m_head_ptr->next;
			destroyNode(m_tail_ptr);
		}
		m_tail_ptr = m_head_ptr;
	}

	/**
	 * pushes a new item into the back of the queue
	 *
	 * @param t the item to add to the back of the queue
	 */
	void push(const T& t) {
		// sleep while MaxSize is exceeded
		if (MaxSize > 0) {
			boost::system_time wakeup_time;
			while (size() >= MaxSize) {
				wakeup_time = boost::get_system_time()
					+ boost::posix_time::millisec(SleepMilliSec);
				boost::thread::sleep(wakeup_time);
			}
		}

		// create a new list node for the queue item
		QueueNode *node_ptr = createNode();
		node_ptr->data = t;
		node_ptr->next = NULL;
		node_ptr->version = 0;

		// append node to the end of the list
		boost::mutex::scoped_lock tail_lock(m_tail_mutex);
		node_ptr->version = (m_next_version += 2);
		m_tail_ptr->next = node_ptr;

		// update the tail pointer for the new node
		m_tail_ptr = node_ptr;

		// only track size if MaxSize > 0
		if (MaxSize > 0)
			++m_size;

		// wake up an idle thread (if any)
		if (m_idle_ptr) {
			IdleThreadInfo *idle_ptr = m_idle_ptr;
			m_idle_ptr = m_idle_ptr->next;
			idle_ptr->wakeup_event.notify_one();
		}
	}

	/**
	 * pops the next item from the top of the queue.  this may cause the calling
	 * thread to sleep until an item is available, and will only return when an
	 * item has been successfully retrieved.
	 *
	 * @param t assigned to the item at the top of the queue
	 * @param thread_info IdleThreadInfo object used to manage the thread
	 */
	void pop(T& t, IdleThreadInfo& thread_info) {
		boost::uint32_t last_known_version;
		while (true) {
			// try to get the next value
			if ( (last_known_version = dequeue(t)) )
				break;	// got an item

			// queue is empty
			boost::mutex::scoped_lock tail_lock(m_tail_mutex);
			if (m_tail_ptr->version == last_known_version) {
				// still empty after acquiring lock
				thread_info.next = m_idle_ptr;
				m_idle_ptr = & thread_info;
				// wait for an item to become available
				thread_info.wakeup_event.wait(tail_lock);
			}
		}
	}

	/**
	 * pops the next item from the top of the queue
	 *
	 * @param t assigned to the item at the top of the queue, if it is not empty
	 *
	 * @return true if an item was retrieved, false if the queue is empty
	 */
	inline bool pop(T& t) { return dequeue(t) != 0; }

private:

#if defined(PION_HAVE_LOCKFREE) && !defined(_MSC_VER)
	/// a caching free list of queue nodes used to reduce memory operations
	boost::lockfree::caching_freelist<QueueNode>	m_free_list;
#endif

	/// mutex used to protect the head pointer to the first item
	boost::mutex					m_head_mutex;

	/// mutex used to protect the tail pointer to the last item
	boost::mutex					m_tail_mutex;

	/// pointer to the first item in the list
	QueueNode *						m_head_ptr;

	/// pointer to the last item in the list
	QueueNode *						m_tail_ptr;

	/// pointer to a list of idle threads waiting for work
	IdleThreadInfo *				m_idle_ptr;

	/// value of the next tail version number
	boost::uint32_t					m_next_version;

	/// used to keep track of the number of items in the queue
	boost::detail::atomic_count		m_size;
};

}	// end namespace pion

#endif
<commit_msg>http://trac.atomiclabs.com/ticket/304 Fixed 100% cpu bug in PionLockedQueue::pop()<commit_after>// -----------------------------------------------------------------------
// pion-common: a collection of common libraries used by the Pion Platform
// -----------------------------------------------------------------------
// Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
//

#ifndef __PION_PIONLOCKEDQUEUE_HEADER__
#define __PION_PIONLOCKEDQUEUE_HEADER__

#include <new>
#include <boost/cstdint.hpp>
#include <boost/noncopyable.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/detail/atomic_count.hpp>
#include <pion/PionConfig.hpp>
#include <pion/PionException.hpp>
#if defined(PION_HAVE_LOCKFREE) && !defined(_MSC_VER)
	#include <boost/lockfree/freelist.hpp>
#endif

// NOTE: the data structures contained in this file are based upon algorithms
// published in the paper "Simple, Fast, and Practical Non-Blocking and Blocking
// Concurrent Queue Algorithms" (1996, Maged M. Michael and Michael L. Scott,
// Department of Computer Science, University of Rochester).
// See http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf

namespace pion {	// begin namespace pion

///
/// PionLockedQueue: a thread-safe, two-lock concurrent FIFO queue
///
template <typename T,
	boost::uint32_t MaxSize = 250000,
	boost::uint32_t SleepMilliSec = 10 >
class PionLockedQueue :
	private boost::noncopyable
{
protected:

	/// data structure used to wrap each item in the queue
	struct QueueNode {
		T		data;		//< data wrapped by the node item
		QueueNode *	next;		//< points to the next node in the queue
		boost::uint32_t	version;	//< the node item's version number
	};

	/// returns a new queue node item for use in the queue
	inline QueueNode *createNode(void) {
#if defined(PION_HAVE_LOCKFREE) && !defined(_MSC_VER)
		return new (m_free_list.allocate()) QueueNode();
#else
		return new QueueNode();
#endif
	}

	/// frees memory for an existing queue node item
	inline void destroyNode(QueueNode *node_ptr) {
#if defined(PION_HAVE_LOCKFREE) && !defined(_MSC_VER)
		node_ptr->~QueueNode();
		m_free_list.deallocate(node_ptr);
#else
		delete node_ptr;
#endif
	}

	/**
	 * dequeues the next item from the top of the queue
	 *
	 * @param t assigned to the item at the top of the queue, if it is not empty
	 * @param boost::uint32_t version number of the item retrieved, or head node if none
	 *
	 * @return true if an item was retrieved; false if the queue is empty
	 */
	inline bool dequeue(T& t, boost::uint32_t& version) {
		// just return if the list is empty
		boost::mutex::scoped_lock head_lock(m_head_mutex);
		QueueNode *new_head_ptr = m_head_ptr->next;
		if (! new_head_ptr) {
			version = m_head_ptr->version;
			return false;
		}

		// get a copy of the item at the head of the list
		version = new_head_ptr->version;
		t = new_head_ptr->data;

		// update the pointer to the head of the list
		QueueNode *old_head_ptr = m_head_ptr;
		m_head_ptr = new_head_ptr;
		head_lock.unlock();

		// free the QueueNode for the old head of the list
		destroyNode(old_head_ptr);

		// only track size if MaxSize > 0
		if (MaxSize > 0)
			--m_size;

		// item successfully dequeued
		return true;
	}

public:

	/// data structure used to manage idle consumer threads waiting for items
	class ConsumerThread {
	public:

		/// constructor assumes thread is active/running
		ConsumerThread(void) : m_is_running(true), m_next_ptr(NULL) {}

		/// returns true while the consumer thread is active/running
		inline bool isRunning(void) const { return m_is_running; }

		/// stops the thread -- if waiting on pop() will return immediately
		inline void stop(void) { m_is_running = false; m_wakeup_event.notify_one(); }

	private:

		/// allow PionLockedQueue direct access to members
		friend class PionLockedQueue;

		volatile bool		m_is_running;		//< true while the thread is running/active
		boost::condition	m_wakeup_event;	//< triggered when a new item is available
		ConsumerThread *	m_next_ptr;		//< pointer to the next idle thread
	};

	/// constructs a new PionLockedQueue
	PionLockedQueue(void)
		: m_head_ptr(NULL), m_tail_ptr(NULL), m_idle_ptr(NULL),
		m_next_version(1), m_size(0)
	{
		// initialize with a dummy node since m_head_ptr is always
		// pointing to the item before the head of the list
		m_head_ptr = m_tail_ptr = createNode();
		m_head_ptr->next = NULL;
		m_head_ptr->version = 0;
	}

	/// virtual destructor
	virtual ~PionLockedQueue() {
		clear();
		destroyNode(m_tail_ptr);
	}

	/// returns true if the queue is empty; false if it is not
	inline bool empty(void) const { return (m_head_ptr->next == NULL); }

	/// returns the number of items that are currently in the queue
	std::size_t size(void) const {
		// only enable size() if MaxSize > 0
		PION_ASSERT(MaxSize > 0);
		return m_size;
	}

	/// clears the list by removing all remaining items
	void clear(void) {
		boost::mutex::scoped_lock tail_lock(m_tail_mutex);
		boost::mutex::scoped_lock head_lock(m_head_mutex);
		while (m_head_ptr->next) {
			m_tail_ptr = m_head_ptr;
			m_head_ptr = m_head_ptr->next;
			destroyNode(m_tail_ptr);
		}
		m_tail_ptr = m_head_ptr;
	}

	/**
	 * pushes a new item into the back of the queue
	 *
	 * @param t the item to add to the back of the queue
	 */
	void push(const T& t) {
		// sleep while MaxSize is exceeded
		if (MaxSize > 0) {
			boost::system_time wakeup_time;
			while (size() >= MaxSize) {
				wakeup_time = boost::get_system_time()
					+ boost::posix_time::millisec(SleepMilliSec);
				boost::thread::sleep(wakeup_time);
			}
		}

		// create a new list node for the queue item
		QueueNode *node_ptr = createNode();
		node_ptr->data = t;
		node_ptr->next = NULL;
		node_ptr->version = 0;

		// append node to the end of the list
		boost::mutex::scoped_lock tail_lock(m_tail_mutex);
		node_ptr->version = (m_next_version += 2);
		m_tail_ptr->next = node_ptr;

		// update the tail pointer for the new node
		m_tail_ptr = node_ptr;

		// only track size if MaxSize > 0
		if (MaxSize > 0)
			++m_size;

		// wake up an idle thread (if any)
		if (m_idle_ptr) {
			ConsumerThread *idle_ptr = m_idle_ptr;
			m_idle_ptr = m_idle_ptr->m_next_ptr;
			idle_ptr->m_wakeup_event.notify_one();
		}
	}

	/**
	 * pops the next item from the top of the queue.  this may cause the calling
	 * thread to sleep until an item is available, and will only return when an
	 * item has been successfully retrieved or when the thread is stopping
	 *
	 * @param t assigned to the item at the top of the queue
	 * @param thread_info ConsumerThread object used to manage the thread
	 *
	 * @return true if an item was retrieved, false if the queue is empty
	 */
	bool pop(T& t, ConsumerThread& thread_info) {
		boost::uint32_t last_known_version;
		while (thread_info.isRunning()) {
			// try to get the next value
			if ( dequeue(t, last_known_version) )
				return true;	// got an item

			// queue is empty
			boost::mutex::scoped_lock tail_lock(m_tail_mutex);
			if (m_tail_ptr->version == last_known_version) {
				// still empty after acquiring lock
				thread_info.m_next_ptr = m_idle_ptr;
				m_idle_ptr = & thread_info;
				// wait for an item to become available
				thread_info.m_wakeup_event.wait(tail_lock);
			}
		}
		return false;
	}

	/**
	 * pops the next item from the top of the queue
	 *
	 * @param t assigned to the item at the top of the queue, if it is not empty
	 *
	 * @return true if an item was retrieved, false if the queue is empty
	 */
	inline bool pop(T& t) { boost::uint32_t version; return dequeue(t, version); }

private:

#if defined(PION_HAVE_LOCKFREE) && !defined(_MSC_VER)
	/// a caching free list of queue nodes used to reduce memory operations
	boost::lockfree::caching_freelist<QueueNode>	m_free_list;
#endif

	/// mutex used to protect the head pointer to the first item
	boost::mutex					m_head_mutex;

	/// mutex used to protect the tail pointer to the last item
	boost::mutex					m_tail_mutex;

	/// pointer to the first item in the list
	QueueNode *						m_head_ptr;

	/// pointer to the last item in the list
	QueueNode *						m_tail_ptr;

	/// pointer to a list of idle threads waiting for work
	ConsumerThread *				m_idle_ptr;

	/// value of the next tail version number
	boost::uint32_t					m_next_version;

	/// used to keep track of the number of items in the queue
	boost::detail::atomic_count		m_size;
};

}	// end namespace pion

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "app/keyboard_code_conversion.h"
#include "base/basictypes.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/string16.h"
#include "chrome/browser/autofill/autofill_common_test.h"
#include "chrome/browser/autofill/autofill_profile.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/net/predictor_api.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

class AutoFillTest : public InProcessBrowserTest {
 protected:
  AutoFillTest() {
    set_show_window(true);
    EnableDOMAutomation();
  }

  void SetUpProfile() {
    autofill_test::DisableSystemServices(browser()->profile());

    AutoFillProfile profile(string16(), 0);
    autofill_test::SetProfileInfo(
        &profile, "Office Space", "Milton", "C.", "Waddams",
        "red.swingline@initech.com", "Initech", "4120 Freidrich Lane",
        "Basement", "Austin", "Texas", "78744", "United States", "5125551234",
        "5125550000");

    PersonalDataManager* personal_data_manager =
        browser()->profile()->GetPersonalDataManager();
    ASSERT_TRUE(personal_data_manager);

    std::vector<AutoFillProfile> profiles(1, profile);
    personal_data_manager->SetProfiles(&profiles);
  }

  void ExpectFieldValue(const std::wstring& field_name,
                        const std::string& expected_value) {
    std::string value;
    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
        browser()->GetSelectedTabContents()->render_view_host(), L"",
        L"window.domAutomationController.send("
        L"document.getElementById('" + field_name + L"').value);", &value));
    EXPECT_EQ(expected_value, value);
  }
};

// Test that basic form fill is working.
// FAILS on windows: http://crbug.com/57962
#if defined(OS_WIN)
#define MAYBE_BasicFormFill DISABLED_BasicFormFill
#else
#define MAYBE_BasicFormFill BasicFormFill
#endif
IN_PROC_BROWSER_TEST_F(AutoFillTest, MAYBE_BasicFormFill) {
  SetUpProfile();

  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
      browser(), GURL("data:text/html;charset=utf-8,"
                      "<form action=\"http://www.google.com/\" method=\"POST\">"
                      "First name:"
                      " <input type=\"text\" id=\"firstname\" />
"
                      "Last name:"
                      " <input type=\"text\" id=\"lastname\" />
"
                      "Address line 1:"
                      " <input type=\"text\" id=\"address1\" />
"
                      "Address line 2:"
                      " <input type=\"text\" id=\"address2\" />
"
                      "City:"
                      " <input type=\"text\" id=\"city\" />
"
                      "State:"
                      " <select id=\"state\">"
                      " <option value=\"\" selected=\"yes\">--</option>"
                      " <option value=\"CA\">California</option>"
                      " <option value=\"TX\">Texas</option>"
                      " </select>
"
                      "ZIP code:"
                      " <input type=\"text\" id=\"zip\" />
"
                      "Country:"
                      " <select id=\"country\">"
                      " <option value=\"\" selected=\"yes\">--</option>"
                      " <option value=\"CA\">Canada</option>"
                      " <option value=\"US\">United States</option>"
                      " </select>
"
                      "Phone number:"
                      " <input type=\"text\" id=\"phone\" />
"
                      "</form>")));

  ASSERT_NO_FATAL_FAILURE(ui_test_utils::ClickOnView(browser(),
                                                     VIEW_ID_TAB_CONTAINER));
  ASSERT_TRUE(ui_test_utils::IsViewFocused(browser(),
                                           VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));

  RenderViewHost* render_view_host =
      browser()->GetSelectedTabContents()->render_view_host();
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
      render_view_host, L"", L"document.getElementById('firstname').focus();"));

  // Start filling the first name field with "M" and wait for the popup to be
  // shown.
  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
      browser(), app::VKEY_M, false, true, false, false,
      NotificationType::AUTOFILL_DID_SHOW_SUGGESTIONS,
      Source<RenderViewHost>(render_view_host)));

  // Press the down arrow to select the suggestion and preview the autofilled
  // form.
  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
      browser(), app::VKEY_DOWN, false, false, false, false,
      NotificationType::AUTOFILL_DID_FILL_FORM_DATA,
      Source<RenderViewHost>(render_view_host)));

  // The previewed values should not be accessible to JavaScript.
  ExpectFieldValue(L"firstname", "M");
  ExpectFieldValue(L"lastname", "");
  ExpectFieldValue(L"address1", "");
  ExpectFieldValue(L"address2", "");
  ExpectFieldValue(L"city", "");
  ExpectFieldValue(L"state", "");
  ExpectFieldValue(L"zip", "");
  ExpectFieldValue(L"country", "");
  ExpectFieldValue(L"phone", "");
  // TODO(isherman): It would be nice to test that the previewed values are
  // displayed: http://crbug.com/57220

  // Press Enter to accept the autofill suggestions.
  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
      browser(), app::VKEY_RETURN, false, false, false, false,
      NotificationType::AUTOFILL_DID_FILL_FORM_DATA,
      Source<RenderViewHost>(render_view_host)));

  // The form should be filled.
  ExpectFieldValue(L"firstname", "Milton");
  ExpectFieldValue(L"lastname", "Waddams");
  ExpectFieldValue(L"address1", "4120 Freidrich Lane");
  ExpectFieldValue(L"address2", "Basement");
  ExpectFieldValue(L"city", "Austin");
  ExpectFieldValue(L"state", "TX");
  ExpectFieldValue(L"zip", "78744");
  ExpectFieldValue(L"country", "US");
  ExpectFieldValue(L"phone", "5125551234");
}
<commit_msg>(Hopefully) fix flakiness for AutoFillTest.BasicFormFill<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "app/keyboard_code_conversion.h"
#include "base/basictypes.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/string16.h"
#include "chrome/browser/autofill/autofill_common_test.h"
#include "chrome/browser/autofill/autofill_profile.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/net/predictor_api.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

class AutoFillTest : public InProcessBrowserTest {
 protected:
  AutoFillTest() {
    set_show_window(true);
    EnableDOMAutomation();
  }

  void SetUpProfile() {
    autofill_test::DisableSystemServices(browser()->profile());

    AutoFillProfile profile(string16(), 0);
    autofill_test::SetProfileInfo(
        &profile, "Office Space", "Milton", "C.", "Waddams",
        "red.swingline@initech.com", "Initech", "4120 Freidrich Lane",
        "Basement", "Austin", "Texas", "78744", "United States", "5125551234",
        "5125550000");

    PersonalDataManager* personal_data_manager =
        browser()->profile()->GetPersonalDataManager();
    ASSERT_TRUE(personal_data_manager);

    std::vector<AutoFillProfile> profiles(1, profile);
    personal_data_manager->SetProfiles(&profiles);
  }

  void ExpectFieldValue(const std::wstring& field_name,
                        const std::string& expected_value) {
    std::string value;
    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
        browser()->GetSelectedTabContents()->render_view_host(), L"",
        L"window.domAutomationController.send("
        L"document.getElementById('" + field_name + L"').value);", &value));
    EXPECT_EQ(expected_value, value);
  }
};

// Test that basic form fill is working.
IN_PROC_BROWSER_TEST_F(AutoFillTest, BasicFormFill) {
  SetUpProfile();

  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
      browser(), GURL("data:text/html;charset=utf-8,"
                      "<form action=\"http://www.google.com/\" method=\"POST\">"
                      "First name:"
                      " <input type=\"text\" id=\"firstname\""
                      "        onFocus=\"domAutomationController.send(true)\""
                      " />
"
                      "Last name:"
                      " <input type=\"text\" id=\"lastname\" />
"
                      "Address line 1:"
                      " <input type=\"text\" id=\"address1\" />
"
                      "Address line 2:"
                      " <input type=\"text\" id=\"address2\" />
"
                      "City:"
                      " <input type=\"text\" id=\"city\" />
"
                      "State:"
                      " <select id=\"state\">"
                      " <option value=\"\" selected=\"yes\">--</option>"
                      " <option value=\"CA\">California</option>"
                      " <option value=\"TX\">Texas</option>"
                      " </select>
"
                      "ZIP code:"
                      " <input type=\"text\" id=\"zip\" />
"
                      "Country:"
                      " <select id=\"country\">"
                      " <option value=\"\" selected=\"yes\">--</option>"
                      " <option value=\"CA\">Canada</option>"
                      " <option value=\"US\">United States</option>"
                      " </select>
"
                      "Phone number:"
                      " <input type=\"text\" id=\"phone\" />
"
                      "</form>")));

  ASSERT_NO_FATAL_FAILURE(ui_test_utils::ClickOnView(browser(),
                                                     VIEW_ID_TAB_CONTAINER));
  ASSERT_TRUE(ui_test_utils::IsViewFocused(browser(),
                                           VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));

  RenderViewHost* render_view_host =
      browser()->GetSelectedTabContents()->render_view_host();
  bool result;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      render_view_host, L"", L"document.getElementById('firstname').focus();",
      &result));
  ASSERT_TRUE(result);

  // Start filling the first name field with "M" and wait for the popup to be
  // shown.
  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
      browser(), app::VKEY_M, false, true, false, false,
      NotificationType::AUTOFILL_DID_SHOW_SUGGESTIONS,
      Source<RenderViewHost>(render_view_host)));

  // Press the down arrow to select the suggestion and preview the autofilled
  // form.
  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
      browser(), app::VKEY_DOWN, false, false, false, false,
      NotificationType::AUTOFILL_DID_FILL_FORM_DATA,
      Source<RenderViewHost>(render_view_host)));

  // The previewed values should not be accessible to JavaScript.
  ExpectFieldValue(L"firstname", "M");
  ExpectFieldValue(L"lastname", "");
  ExpectFieldValue(L"address1", "");
  ExpectFieldValue(L"address2", "");
  ExpectFieldValue(L"city", "");
  ExpectFieldValue(L"state", "");
  ExpectFieldValue(L"zip", "");
  ExpectFieldValue(L"country", "");
  ExpectFieldValue(L"phone", "");
  // TODO(isherman): It would be nice to test that the previewed values are
  // displayed: http://crbug.com/57220

  // Press Enter to accept the autofill suggestions.
  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
      browser(), app::VKEY_RETURN, false, false, false, false,
      NotificationType::AUTOFILL_DID_FILL_FORM_DATA,
      Source<RenderViewHost>(render_view_host)));

  // The form should be filled.
  ExpectFieldValue(L"firstname", "Milton");
  ExpectFieldValue(L"lastname", "Waddams");
  ExpectFieldValue(L"address1", "4120 Freidrich Lane");
  ExpectFieldValue(L"address2", "Basement");
  ExpectFieldValue(L"city", "Austin");
  ExpectFieldValue(L"state", "TX");
  ExpectFieldValue(L"zip", "78744");
  ExpectFieldValue(L"country", "US");
  ExpectFieldValue(L"phone", "5125551234");
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <QKeyEvent>

#include <OgreRay.h>
#include <OgreSceneManager.h>
#include <OgreCamera.h>
#include <OgreMovableObject.h>
#include <OgreRectangle2D.h>
#include <OgreSceneNode.h>
#include <OgreViewport.h>
#include <OgreMaterialManager.h>
#include <OgreTexture.h>
#include <OgreTextureManager.h>

#include <ros/time.h>

#include "move_tool.h"

#include "rviz/ogre_helpers/camera_base.h"
#include "rviz/ogre_helpers/qt_ogre_render_window.h"
#include "rviz/selection/selection_manager.h"
#include "rviz/visualization_manager.h"
#include "rviz/render_panel.h"
#include "rviz/display.h"
#include "rviz/viewport_mouse_event.h"
#include "rviz/load_resource.h"

#include "selection_tool.h"

namespace rviz
{

SelectionTool::SelectionTool()
  : Tool()
  , move_tool_( new MoveTool() )
  , selecting_( false )
  , sel_start_x_( 0 )
  , sel_start_y_( 0 )
  , moving_( false )
{
  shortcut_key_ = 's';
}

SelectionTool::~SelectionTool()
{
  delete move_tool_;
}

void SelectionTool::onInitialize()
{
  move_tool_->initialize( context_ );
}

void SelectionTool::activate()
{
  setStatus( "Click and drag to select objects on the screen." );
  context_->getSelectionManager()->setTextureSize(512);
  selecting_ = false;
  moving_ = false;
//  context_->getSelectionManager()->enableInteraction(true);
}

void SelectionTool::deactivate()
{
  context_->getSelectionManager()->removeHighlight();
}

void SelectionTool::update(float wall_dt, float ros_dt)
{
  SelectionManager* sel_manager = context_->getSelectionManager();

  if (!selecting_)
  {
    sel_manager->removeHighlight();
  }
}

int SelectionTool::processMouseEvent( ViewportMouseEvent& event )
{
  SelectionManager* sel_manager = context_->getSelectionManager();

  int flags = 0;

  if( event.alt() )
  {
    moving_ = true;
    selecting_ = false;
  }
  else
  {
    moving_ = false;

    if( event.leftDown() )
    {
      selecting_ = true;

      sel_start_x_ = event.x;
      sel_start_y_ = event.y;
    }
  }

  if( selecting_ )
  {
    sel_manager->highlight( event.viewport, sel_start_x_, sel_start_y_, event.x, event.y );

    if( event.leftUp() )
    {
      SelectionManager::SelectType type = SelectionManager::Replace;

      M_Picked selection;

      if( event.shift() )
      {
        type = SelectionManager::Add;
      }
      else if( event.control() )
      {
        type = SelectionManager::Remove;
      }

      sel_manager->select( event.viewport, sel_start_x_, sel_start_y_, event.x, event.y, type );

      selecting_ = false;
    }

    flags |= Render;
  }
  else if( moving_ )
  {
    sel_manager->removeHighlight();

    flags = move_tool_->processMouseEvent( event );

    if( event.type == QEvent::MouseButtonRelease )
    {
      moving_ = false;
    }
  }
  else
  {
    sel_manager->highlight( event.viewport, event.x, event.y, event.x, event.y );
  }

  return flags;
}

int SelectionTool::processKeyEvent( QKeyEvent* event, RenderPanel* panel )
{
  SelectionManager* sel_manager = context_->getSelectionManager();

  if( event->key() == Qt::Key_F )
  {
    sel_manager->focusOnSelection();
  }

  return Render;
}

} // end namespace rviz

#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS( rviz::SelectionTool, rviz::Tool )
<commit_msg>made 'selection' tool access all keys<commit_after>/*
 * Copyright (c) 2008, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <QKeyEvent>

#include <OgreRay.h>
#include <OgreSceneManager.h>
#include <OgreCamera.h>
#include <OgreMovableObject.h>
#include <OgreRectangle2D.h>
#include <OgreSceneNode.h>
#include <OgreViewport.h>
#include <OgreMaterialManager.h>
#include <OgreTexture.h>
#include <OgreTextureManager.h>

#include <ros/time.h>

#include "move_tool.h"

#include "rviz/ogre_helpers/camera_base.h"
#include "rviz/ogre_helpers/qt_ogre_render_window.h"
#include "rviz/selection/selection_manager.h"
#include "rviz/visualization_manager.h"
#include "rviz/render_panel.h"
#include "rviz/display.h"
#include "rviz/viewport_mouse_event.h"
#include "rviz/load_resource.h"

#include "selection_tool.h"

namespace rviz
{

SelectionTool::SelectionTool()
  : Tool()
  , move_tool_( new MoveTool() )
  , selecting_( false )
  , sel_start_x_( 0 )
  , sel_start_y_( 0 )
  , moving_( false )
{
  shortcut_key_ = 's';
  access_all_keys_ = true;
}

SelectionTool::~SelectionTool()
{
  delete move_tool_;
}

void SelectionTool::onInitialize()
{
  move_tool_->initialize( context_ );
}

void SelectionTool::activate()
{
  setStatus( "Click and drag to select objects on the screen." );
  context_->getSelectionManager()->setTextureSize(512);
  selecting_ = false;
  moving_ = false;
//  context_->getSelectionManager()->enableInteraction(true);
}

void SelectionTool::deactivate()
{
  context_->getSelectionManager()->removeHighlight();
}

void SelectionTool::update(float wall_dt, float ros_dt)
{
  SelectionManager* sel_manager = context_->getSelectionManager();

  if (!selecting_)
  {
    sel_manager->removeHighlight();
  }
}

int SelectionTool::processMouseEvent( ViewportMouseEvent& event )
{
  SelectionManager* sel_manager = context_->getSelectionManager();

  int flags = 0;

  if( event.alt() )
  {
    moving_ = true;
    selecting_ = false;
  }
  else
  {
    moving_ = false;

    if( event.leftDown() )
    {
      selecting_ = true;

      sel_start_x_ = event.x;
      sel_start_y_ = event.y;
    }
  }

  if( selecting_ )
  {
    sel_manager->highlight( event.viewport, sel_start_x_, sel_start_y_, event.x, event.y );

    if( event.leftUp() )
    {
      SelectionManager::SelectType type = SelectionManager::Replace;

      M_Picked selection;

      if( event.shift() )
      {
        type = SelectionManager::Add;
      }
      else if( event.control() )
      {
        type = SelectionManager::Remove;
      }

      sel_manager->select( event.viewport, sel_start_x_, sel_start_y_, event.x, event.y, type );

      selecting_ = false;
    }

    flags |= Render;
  }
  else if( moving_ )
  {
    sel_manager->removeHighlight();

    flags = move_tool_->processMouseEvent( event );

    if( event.type == QEvent::MouseButtonRelease )
    {
      moving_ = false;
    }
  }
  else
  {
    sel_manager->highlight( event.viewport, event.x, event.y, event.x, event.y );
  }

  return flags;
}

int SelectionTool::processKeyEvent( QKeyEvent* event, RenderPanel* panel )
{
  SelectionManager* sel_manager = context_->getSelectionManager();

  if( event->key() == Qt::Key_F )
  {
    sel_manager->focusOnSelection();
  }

  return Render;
}

} // end namespace rviz

#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS( rviz::SelectionTool, rviz::Tool )
<|endoftext|>