<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: actiontriggerhelper.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 01:22:39 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef __FRAMEWORK_HELPER_ACTIONTRIGGERHELPER_HXX_
#include <helper/actiontriggerhelper.hxx>
#endif

#ifndef __FRAMEWORK_CLASSES_ACTIONTRIGGERSEPARATORPROPERTYSET_HXX_
#include <classes/actiontriggerseparatorpropertyset.hxx>
#endif

#ifndef __FRAMEWORK_CLASSES_ROOTACTIONTRIGGERCONTAINER_HXX_
#include <classes/rootactiontriggercontainer.hxx>
#endif

#ifndef __FRAMEWORK_CLASSES_IMAGEWRAPPER_HXX_
#include <classes/imagewrapper.hxx>
#endif

#ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
#include <com/sun/star/lang/XServiceInfo.hpp>
#endif

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif

#ifndef _COM_SUN_STAR_AWT_XBITMAP_HPP_
#include <com/sun/star/awt/XBitmap.hpp>
#endif

#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif

#ifndef _VOS_MUTEX_HXX_
#include <vos/mutex.hxx>
#endif

#ifndef _STREAM_HXX
#include <tools/stream.hxx>
#endif

#ifndef _CPPUHELPER_WEAK_HXX_
#include <cppuhelper/weak.hxx>
#endif

#ifndef _COMPHELPER_PROCESSFACTORY_HXX_
#include <comphelper/processfactory.hxx>
#endif

const USHORT START_ITEMID = 1000;

using namespace rtl;
using namespace vos;
using namespace com::sun::star::awt;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::beans;
using namespace com::sun::star::container;

namespace framework
{

// ----------------------------------------------------------------------------
// implementation helper ( menu => ActionTrigger )
// ----------------------------------------------------------------------------

sal_Bool IsSeparator( Reference< XPropertySet > xPropertySet )
{
    Reference< XServiceInfo > xServiceInfo( xPropertySet, UNO_QUERY );
    try
    {
        return xServiceInfo->supportsService( OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICENAME_ACTIONTRIGGERSEPARATOR )) );
    }
    catch ( Exception& )
    {
    }

    return sal_False;
}

void GetMenuItemAttributes( Reference< XPropertySet > xActionTriggerPropertySet,
                            OUString& aMenuLabel,
                            OUString& aCommandURL,
                            OUString& aHelpURL,
                            Reference< XBitmap >& xBitmap,
                            Reference< XIndexContainer >& xSubContainer )
{
    Any a;

    try
    {
        // mandatory properties
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" )) );
        a >>= aMenuLabel;
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "CommandURL" )) );
        a >>= aCommandURL;
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Image" )) );
        a >>= xBitmap;
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "SubContainer" )) );
        a >>= xSubContainer;
    }
    catch ( Exception& )
    {
    }

    // optional properties
    try
    {
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "HelpURL" )) );
        a >>= aHelpURL;
    }
    catch ( Exception& )
    {
    }
}

void InsertSubMenuItems( Menu* pSubMenu, USHORT& nItemId, Reference< XIndexContainer > xActionTriggerContainer )
{
    Reference< XIndexAccess > xIndexAccess( xActionTriggerContainer, UNO_QUERY );
    if ( xIndexAccess.is() )
    {
        OUString aSlotURL( RTL_CONSTASCII_USTRINGPARAM( "slot:" ));

        for ( sal_Int32 i = 0; i < xIndexAccess->getCount(); i++ )
        {
            try
            {
                Reference< XPropertySet > xPropSet;
                Any a = xIndexAccess->getByIndex( i );

                if (( a >>= xPropSet ) && ( xPropSet.is() ))
                {
                    if ( IsSeparator( xPropSet ))
                    {
                        // Separator
                        OGuard aGuard( Application::GetSolarMutex() );
                        pSubMenu->InsertSeparator();
                    }
                    else
                    {
                        // Menu item
                        OUString aLabel;
                        OUString aCommandURL;
                        OUString aHelpURL;
                        Reference< XBitmap > xBitmap;
                        Reference< XIndexContainer > xSubContainer;
                        sal_Bool bSpecialItemId = sal_False;

                        USHORT nNewItemId = nItemId++;
                        GetMenuItemAttributes( xPropSet, aLabel, aCommandURL, aHelpURL, xBitmap, xSubContainer );

                        OGuard aGuard( Application::GetSolarMutex() );
                        {
                            // insert new menu item
                            sal_Int32 nIndex = aCommandURL.indexOf( aSlotURL );
                            if ( nIndex >= 0 )
                            {
                                // Special code for our menu implementation: some menu items don't have a
                                // command url but uses the item id as a unqiue identifier. These entries
                                // got a special url during conversion from menu=>actiontriggercontainer.
                                // Now we have to extract this special url and set the correct item id!!!
                                bSpecialItemId = sal_True;
                                nNewItemId = (USHORT)aCommandURL.copy( nIndex+aSlotURL.getLength() ).toInt32();
                                pSubMenu->InsertItem( nNewItemId, aLabel );
                            }
                            else
                            {
                                pSubMenu->InsertItem( nNewItemId, aLabel );
                                pSubMenu->SetItemCommand( nNewItemId, aCommandURL );
                            }

                            // handle bitmap
                            if ( xBitmap.is() )
                            {
                                sal_Bool bImageSet = sal_False;

                                Reference< XUnoTunnel > xUnoTunnel( xBitmap, UNO_QUERY );
                                if ( xUnoTunnel.is() )
                                {
                                    // Try to get implementation pointer through XUnoTunnel
                                    sal_Int64 nPointer = xUnoTunnel->getSomething( ImageWrapper::GetUnoTunnelId() );
                                    if ( nPointer )
                                    {
                                        // This is our own optimized implementation of menu images!
                                        ImageWrapper* pImageWrapper = (ImageWrapper *)nPointer;
                                        Image aMenuImage = pImageWrapper->GetImage();

                                        if ( !!aMenuImage )
                                            pSubMenu->SetItemImage( nNewItemId, aMenuImage );

                                        bImageSet = sal_True;
                                    }
                                }

                                if ( !bImageSet )
                                {
                                    // This is a unknown implementation of XBitmap interface. We have to
                                    // use a more time consuming way to build an Image!
                                    // TODO: use memory streams to build a bitmap and this can be used
                                    // to create an image!!
                                    Image   aImage;
                                    Bitmap  aBitmap;

                                    Sequence< sal_Int8 > aDIBSeq;
                                    {
                                        aDIBSeq = xBitmap->getDIB();
                                        SvMemoryStream aMem( (void *)aDIBSeq.getConstArray(), aDIBSeq.getLength(), STREAM_READ );
                                        aMem >> aBitmap;
                                    }

                                    aDIBSeq = xBitmap->getMaskDIB();
                                    if ( aDIBSeq.getLength() > 0 )
                                    {
                                        Bitmap aMaskBitmap;
                                        SvMemoryStream aMem( (void *)aDIBSeq.getConstArray(), aDIBSeq.getLength(), STREAM_READ );
                                        aMem >> aMaskBitmap;
                                        aImage = Image( aBitmap, aMaskBitmap );
                                    }
                                    else
                                        aImage = Image( aBitmap );
                                }
                            }

                            if ( xSubContainer.is() )
                            {
                                PopupMenu* pNewSubMenu = new PopupMenu;

                                // Sub menu (recursive call CreateSubMenu )
                                InsertSubMenuItems( pNewSubMenu, nItemId, xSubContainer );
                                pSubMenu->SetPopupMenu( nNewItemId, pNewSubMenu );
                            }
                        }
                    }
                }
            }
            catch ( IndexOutOfBoundsException )
            {
                return;
            }
            catch ( WrappedTargetException )
            {
                return;
            }
            catch ( RuntimeException )
            {
                return;
            }
        }
    }
}

// ----------------------------------------------------------------------------
// implementation helper ( ActionTrigger => menu )
// ----------------------------------------------------------------------------

Reference< XPropertySet > CreateActionTrigger( USHORT nItemId, const Menu* pMenu, const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
{
    Reference< XPropertySet > xPropSet;

    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
    if ( xMultiServiceFactory.is() )
    {
        xPropSet = Reference< XPropertySet >(   xMultiServiceFactory->createInstance(
                                                    OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTrigger" )) ),
                                                UNO_QUERY );

        Any a;

        try
        {
            // Retrieve the menu attributes and set them in our PropertySet
            OUString aLabel = pMenu->GetItemText( nItemId );
            a <<= aLabel;
            xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" )), a );

            OUString aCommandURL = pMenu->GetItemCommand( nItemId );

            if ( aCommandURL.getLength() == 0 )
            {
                aCommandURL = OUString( RTL_CONSTASCII_USTRINGPARAM( "slot:" ));
                aCommandURL += OUString::valueOf( (sal_Int32)nItemId );
            }

            a <<= aCommandURL;
            xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "CommandURL" )), a );

            Image aImage = pMenu->GetItemImage( nItemId );
            if ( !!aImage )
            {
                // We use our own optimized XBitmap implementation
                Reference< XBitmap > xBitmap( static_cast< cppu::OWeakObject* >( new ImageWrapper( aImage )), UNO_QUERY );
                a <<= xBitmap;
                xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Image" )), a );
            }
        }
        catch ( Exception& )
        {
        }
    }

    return xPropSet;
}

Reference< XPropertySet > CreateActionTriggerSeparator( const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
{
    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
    if ( xMultiServiceFactory.is() )
    {
        return Reference< XPropertySet >(   xMultiServiceFactory->createInstance(
                                                OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTriggerSeparator" )) ),
                                            UNO_QUERY );
    }

    return Reference< XPropertySet >();
}

Reference< XIndexContainer > CreateActionTriggerContainer( const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
{
    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
    if ( xMultiServiceFactory.is() )
    {
        return Reference< XIndexContainer >( xMultiServiceFactory->createInstance(
                                                OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTriggerContainer" )) ),
                                             UNO_QUERY );
    }

    return Reference< XIndexContainer >();
}

void FillActionTriggerContainerWithMenu( const Menu* pMenu, Reference< XIndexContainer >& rActionTriggerContainer )
{
    OGuard aGuard( Application::GetSolarMutex() );

    for ( USHORT nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
    {
        USHORT          nItemId = pMenu->GetItemId( nPos );
        MenuItemType    nType   = pMenu->GetItemType( nPos );

        try
        {
            Any a;
            Reference< XPropertySet > xPropSet;

            if ( nType == MENUITEM_SEPARATOR )
            {
                xPropSet = CreateActionTriggerSeparator( rActionTriggerContainer );

                a <<= xPropSet;
                rActionTriggerContainer->insertByIndex( nPos, a );
            }
            else
            {
                xPropSet = CreateActionTrigger( nItemId, pMenu, rActionTriggerContainer );

                a <<= xPropSet;
                rActionTriggerContainer->insertByIndex( nPos, a );

                PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nItemId );
                if ( pPopupMenu )
                {
                    // recursive call to build next sub menu
                    Any a;
                    Reference< XIndexContainer > xSubContainer = CreateActionTriggerContainer( rActionTriggerContainer );

                    a <<= xSubContainer;
                    xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "SubContainer" )), a );
                    FillActionTriggerContainerWithMenu( pPopupMenu, xSubContainer );
                }
            }
        }
        catch ( Exception& )
        {
        }
    }
}

void ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
    Menu* pNewMenu,
    const Reference< XIndexContainer >& rActionTriggerContainer )
{
    USHORT nItemId = START_ITEMID;

    if ( rActionTriggerContainer.is() )
        InsertSubMenuItems( pNewMenu, nItemId, rActionTriggerContainer );
}

void ActionTriggerHelper::FillActionTriggerContainerFromMenu(
    Reference< XIndexContainer >& xActionTriggerContainer,
    const Menu* pMenu )
{
    FillActionTriggerContainerWithMenu( pMenu, xActionTriggerContainer );
}

Reference< XIndexContainer > ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
    // #110897#
    const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xServiceFactory,
    const Menu* pMenu )
{
    return new RootActionTriggerContainer( pMenu, xServiceFactory );
}

}
<commit_msg>INTEGRATION: CWS fwk25 (1.4.22); FILE MERGED 2005/10/14 14:03:42 cd 1.4.22.1: #126010# Use self-defined XBitmap references from context menu interceptor<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: actiontriggerhelper.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: kz $ $Date: 2005-11-04 15:42:33 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef __FRAMEWORK_HELPER_ACTIONTRIGGERHELPER_HXX_
#include <helper/actiontriggerhelper.hxx>
#endif

#ifndef __FRAMEWORK_CLASSES_ACTIONTRIGGERSEPARATORPROPERTYSET_HXX_
#include <classes/actiontriggerseparatorpropertyset.hxx>
#endif

#ifndef __FRAMEWORK_CLASSES_ROOTACTIONTRIGGERCONTAINER_HXX_
#include <classes/rootactiontriggercontainer.hxx>
#endif

#ifndef __FRAMEWORK_CLASSES_IMAGEWRAPPER_HXX_
#include <classes/imagewrapper.hxx>
#endif

#ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
#include <com/sun/star/lang/XServiceInfo.hpp>
#endif

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif

#ifndef _COM_SUN_STAR_AWT_XBITMAP_HPP_
#include <com/sun/star/awt/XBitmap.hpp>
#endif

#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif

#ifndef _VOS_MUTEX_HXX_
#include <vos/mutex.hxx>
#endif

#ifndef _STREAM_HXX
#include <tools/stream.hxx>
#endif

#ifndef _CPPUHELPER_WEAK_HXX_
#include <cppuhelper/weak.hxx>
#endif

#ifndef _COMPHELPER_PROCESSFACTORY_HXX_
#include <comphelper/processfactory.hxx>
#endif

const USHORT START_ITEMID = 1000;

using namespace rtl;
using namespace vos;
using namespace com::sun::star::awt;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::beans;
using namespace com::sun::star::container;

namespace framework
{

// ----------------------------------------------------------------------------
// implementation helper ( menu => ActionTrigger )
// ----------------------------------------------------------------------------

sal_Bool IsSeparator( Reference< XPropertySet > xPropertySet )
{
    Reference< XServiceInfo > xServiceInfo( xPropertySet, UNO_QUERY );
    try
    {
        return xServiceInfo->supportsService( OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICENAME_ACTIONTRIGGERSEPARATOR )) );
    }
    catch ( Exception& )
    {
    }

    return sal_False;
}

void GetMenuItemAttributes( Reference< XPropertySet > xActionTriggerPropertySet,
                            OUString& aMenuLabel,
                            OUString& aCommandURL,
                            OUString& aHelpURL,
                            Reference< XBitmap >& xBitmap,
                            Reference< XIndexContainer >& xSubContainer )
{
    Any a;

    try
    {
        // mandatory properties
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" )) );
        a >>= aMenuLabel;
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "CommandURL" )) );
        a >>= aCommandURL;
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Image" )) );
        a >>= xBitmap;
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "SubContainer" )) );
        a >>= xSubContainer;
    }
    catch ( Exception& )
    {
    }

    // optional properties
    try
    {
        a = xActionTriggerPropertySet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "HelpURL" )) );
        a >>= aHelpURL;
    }
    catch ( Exception& )
    {
    }
}

void InsertSubMenuItems( Menu* pSubMenu, USHORT& nItemId, Reference< XIndexContainer > xActionTriggerContainer )
{
    Reference< XIndexAccess > xIndexAccess( xActionTriggerContainer, UNO_QUERY );
    if ( xIndexAccess.is() )
    {
        OUString aSlotURL( RTL_CONSTASCII_USTRINGPARAM( "slot:" ));

        for ( sal_Int32 i = 0; i < xIndexAccess->getCount(); i++ )
        {
            try
            {
                Reference< XPropertySet > xPropSet;
                Any a = xIndexAccess->getByIndex( i );

                if (( a >>= xPropSet ) && ( xPropSet.is() ))
                {
                    if ( IsSeparator( xPropSet ))
                    {
                        // Separator
                        OGuard aGuard( Application::GetSolarMutex() );
                        pSubMenu->InsertSeparator();
                    }
                    else
                    {
                        // Menu item
                        OUString aLabel;
                        OUString aCommandURL;
                        OUString aHelpURL;
                        Reference< XBitmap > xBitmap;
                        Reference< XIndexContainer > xSubContainer;
                        sal_Bool bSpecialItemId = sal_False;

                        USHORT nNewItemId = nItemId++;
                        GetMenuItemAttributes( xPropSet, aLabel, aCommandURL, aHelpURL, xBitmap, xSubContainer );

                        OGuard aGuard( Application::GetSolarMutex() );
                        {
                            // insert new menu item
                            sal_Int32 nIndex = aCommandURL.indexOf( aSlotURL );
                            if ( nIndex >= 0 )
                            {
                                // Special code for our menu implementation: some menu items don't have a
                                // command url but uses the item id as a unqiue identifier. These entries
                                // got a special url during conversion from menu=>actiontriggercontainer.
                                // Now we have to extract this special url and set the correct item id!!!
                                bSpecialItemId = sal_True;
                                nNewItemId = (USHORT)aCommandURL.copy( nIndex+aSlotURL.getLength() ).toInt32();
                                pSubMenu->InsertItem( nNewItemId, aLabel );
                            }
                            else
                            {
                                pSubMenu->InsertItem( nNewItemId, aLabel );
                                pSubMenu->SetItemCommand( nNewItemId, aCommandURL );
                            }

                            // handle bitmap
                            if ( xBitmap.is() )
                            {
                                sal_Bool bImageSet = sal_False;

                                Reference< XUnoTunnel > xUnoTunnel( xBitmap, UNO_QUERY );
                                if ( xUnoTunnel.is() )
                                {
                                    // Try to get implementation pointer through XUnoTunnel
                                    sal_Int64 nPointer = xUnoTunnel->getSomething( ImageWrapper::GetUnoTunnelId() );
                                    if ( nPointer )
                                    {
                                        // This is our own optimized implementation of menu images!
                                        ImageWrapper* pImageWrapper = (ImageWrapper *)nPointer;
                                        Image aMenuImage = pImageWrapper->GetImage();

                                        if ( !!aMenuImage )
                                            pSubMenu->SetItemImage( nNewItemId, aMenuImage );

                                        bImageSet = sal_True;
                                    }
                                }

                                if ( !bImageSet )
                                {
                                    // This is an unknown implementation of a XBitmap interface. We have to
                                    // use a more time consuming way to build an Image!
                                    Image   aImage;
                                    Bitmap  aBitmap;

                                    Sequence< sal_Int8 > aDIBSeq;
                                    {
                                        aDIBSeq = xBitmap->getDIB();
                                        SvMemoryStream aMem( (void *)aDIBSeq.getConstArray(), aDIBSeq.getLength(), STREAM_READ );
                                        aMem >> aBitmap;
                                    }

                                    aDIBSeq = xBitmap->getMaskDIB();
                                    if ( aDIBSeq.getLength() > 0 )
                                    {
                                        Bitmap aMaskBitmap;
                                        SvMemoryStream aMem( (void *)aDIBSeq.getConstArray(), aDIBSeq.getLength(), STREAM_READ );
                                        aMem >> aMaskBitmap;
                                        aImage = Image( aBitmap, aMaskBitmap );
                                    }
                                    else
                                        aImage = Image( aBitmap );

                                    if ( !!aImage )
                                        pSubMenu->SetItemImage( nNewItemId, aImage );
                                }
                            }

                            if ( xSubContainer.is() )
                            {
                                PopupMenu* pNewSubMenu = new PopupMenu;

                                // Sub menu (recursive call CreateSubMenu )
                                InsertSubMenuItems( pNewSubMenu, nItemId, xSubContainer );
                                pSubMenu->SetPopupMenu( nNewItemId, pNewSubMenu );
                            }
                        }
                    }
                }
            }
            catch ( IndexOutOfBoundsException )
            {
                return;
            }
            catch ( WrappedTargetException )
            {
                return;
            }
            catch ( RuntimeException )
            {
                return;
            }
        }
    }
}

// ----------------------------------------------------------------------------
// implementation helper ( ActionTrigger => menu )
// ----------------------------------------------------------------------------

Reference< XPropertySet > CreateActionTrigger( USHORT nItemId, const Menu* pMenu, const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
{
    Reference< XPropertySet > xPropSet;

    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
    if ( xMultiServiceFactory.is() )
    {
        xPropSet = Reference< XPropertySet >(   xMultiServiceFactory->createInstance(
                                                    OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTrigger" )) ),
                                                UNO_QUERY );

        Any a;

        try
        {
            // Retrieve the menu attributes and set them in our PropertySet
            OUString aLabel = pMenu->GetItemText( nItemId );
            a <<= aLabel;
            xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" )), a );

            OUString aCommandURL = pMenu->GetItemCommand( nItemId );

            if ( aCommandURL.getLength() == 0 )
            {
                aCommandURL = OUString( RTL_CONSTASCII_USTRINGPARAM( "slot:" ));
                aCommandURL += OUString::valueOf( (sal_Int32)nItemId );
            }

            a <<= aCommandURL;
            xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "CommandURL" )), a );

            Image aImage = pMenu->GetItemImage( nItemId );
            if ( !!aImage )
            {
                // We use our own optimized XBitmap implementation
                Reference< XBitmap > xBitmap( static_cast< cppu::OWeakObject* >( new ImageWrapper( aImage )), UNO_QUERY );
                a <<= xBitmap;
                xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Image" )), a );
            }
        }
        catch ( Exception& )
        {
        }
    }

    return xPropSet;
}

Reference< XPropertySet > CreateActionTriggerSeparator( const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
{
    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
    if ( xMultiServiceFactory.is() )
    {
        return Reference< XPropertySet >(   xMultiServiceFactory->createInstance(
                                                OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTriggerSeparator" )) ),
                                            UNO_QUERY );
    }

    return Reference< XPropertySet >();
}

Reference< XIndexContainer > CreateActionTriggerContainer( const Reference< XIndexContainer >& rActionTriggerContainer ) throw ( RuntimeException )
{
    Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
    if ( xMultiServiceFactory.is() )
    {
        return Reference< XIndexContainer >( xMultiServiceFactory->createInstance(
                                                OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.ActionTriggerContainer" )) ),
                                             UNO_QUERY );
    }

    return Reference< XIndexContainer >();
}

void FillActionTriggerContainerWithMenu( const Menu* pMenu, Reference< XIndexContainer >& rActionTriggerContainer )
{
    OGuard aGuard( Application::GetSolarMutex() );

    for ( USHORT nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
    {
        USHORT          nItemId = pMenu->GetItemId( nPos );
        MenuItemType    nType   = pMenu->GetItemType( nPos );

        try
        {
            Any a;
            Reference< XPropertySet > xPropSet;

            if ( nType == MENUITEM_SEPARATOR )
            {
                xPropSet = CreateActionTriggerSeparator( rActionTriggerContainer );

                a <<= xPropSet;
                rActionTriggerContainer->insertByIndex( nPos, a );
            }
            else
            {
                xPropSet = CreateActionTrigger( nItemId, pMenu, rActionTriggerContainer );

                a <<= xPropSet;
                rActionTriggerContainer->insertByIndex( nPos, a );

                PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nItemId );
                if ( pPopupMenu )
                {
                    // recursive call to build next sub menu
                    Any a;
                    Reference< XIndexContainer > xSubContainer = CreateActionTriggerContainer( rActionTriggerContainer );

                    a <<= xSubContainer;
                    xPropSet->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "SubContainer" )), a );
                    FillActionTriggerContainerWithMenu( pPopupMenu, xSubContainer );
                }
            }
        }
        catch ( Exception& )
        {
        }
    }
}

void ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
    Menu* pNewMenu,
    const Reference< XIndexContainer >& rActionTriggerContainer )
{
    USHORT nItemId = START_ITEMID;

    if ( rActionTriggerContainer.is() )
        InsertSubMenuItems( pNewMenu, nItemId, rActionTriggerContainer );
}

void ActionTriggerHelper::FillActionTriggerContainerFromMenu(
    Reference< XIndexContainer >& xActionTriggerContainer,
    const Menu* pMenu )
{
    FillActionTriggerContainerWithMenu( pMenu, xActionTriggerContainer );
}

Reference< XIndexContainer > ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
    // #110897#
    const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xServiceFactory,
    const Menu* pMenu )
{
    return new RootActionTriggerContainer( pMenu, xServiceFactory );
}

}
<|endoftext|>


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

#include "aistreams/gstreamer/gstreamer_runner.h"

#include <gst/app/gstappsink.h>
#include <gst/gst.h>

#include <algorithm>
#include <thread>
#include <utility>

#include "absl/strings/str_format.h"
#include "aistreams/gstreamer/gstreamer_utils.h"
#include "aistreams/port/canonical_errors.h"
#include "aistreams/port/logging.h"
#include "aistreams/port/status.h"
#include "aistreams/port/status_macros.h"

namespace aistreams {

namespace {

constexpr char kAppSrcName[] = "feed";
constexpr char kAppSinkName[] = "fetch";

// Callback attached to the GLib main loop.
gboolean gst_bus_message_callback(GstBus* bus, GstMessage* message,
                                  GMainLoop* loop) {
  GError* err;
  gchar* debug_info;
  switch (GST_MESSAGE_TYPE(message)) {
    case GST_MESSAGE_EOS:
      g_main_loop_quit(loop);
      break;
    case GST_MESSAGE_ERROR:
      gst_message_parse_error(message, &err, &debug_info);
      LOG(ERROR) << absl::StrFormat("Error from gstreamer element %s: %s",
                                    GST_OBJECT_NAME(message->src),
                                    err->message);
      LOG(ERROR) << absl::StrFormat("Additional debug info: %s",
                                    debug_info ? debug_info : "none");
      LOG(ERROR) << "Got gstreamer error; shutting down event loop";
      g_main_loop_quit(loop);
      break;
    default:
      break;
  }
  return TRUE;
}

// Callback for receiving new GstSample's from appsink.
GstFlowReturn on_new_sample_from_sink(
    GstElement* elt, GstreamerRunner::ReceiverCallback* receiver_callback) {
  // Get the GstSample from appsink.
  GstSample* sample = gst_app_sink_pull_sample(GST_APP_SINK(elt));

  // No-op if callbacks are not supplied.
  if (receiver_callback == nullptr || !(*receiver_callback)) {
    gst_sample_unref(sample);
    return GST_FLOW_OK;
  }

  // Copy the GstSample into aistreamer's GstreamerBuffer type.
  GstreamerBuffer gstreamer_buffer;

  GstCaps* caps = gst_sample_get_caps(sample);
  gchar* caps_string = gst_caps_to_string(caps);
  gstreamer_buffer.set_caps_string(caps_string);
  g_free(caps_string);

  GstBuffer* buffer = gst_sample_get_buffer(sample);
  GstMapInfo map;
  gst_buffer_map(buffer, &map, GST_MAP_READ);
  gstreamer_buffer.assign(reinterpret_cast<char*>(map.data), map.size);
  gst_buffer_unmap(buffer, &map);
  gst_sample_unref(sample);

  // Deliver the GstreamerBuffer using the callback.
  // TODO: Decide on special status codes to pause/halt the pipeline.
  Status status = (*receiver_callback)(std::move(gstreamer_buffer));
  if (!status.ok()) {
    LOG(ERROR) << status.message();
  }
  return GST_FLOW_OK;
}

Status ValidateRunnerOptions(const GstreamerRunnerOptions& options) {
  if (options.processing_pipeline_string.empty()) {
    return InvalidArgumentError("Given an empty processing pipeline string");
  }
  if (options.appsrc_caps_string.empty()) {
    return InvalidArgumentError("Given an empty appsrc caps string");
  }
  return OkStatus();
}

}  // namespace

// A class that manages the runtime/context of a Gstreamer pipeline.
class GstreamerRunner::GstreamerRuntimeImpl {
 public:
  struct Options {
    std::string processing_pipeline_string;
    std::string appsrc_caps_string;
    ReceiverCallback receiver_callback;
  };

  GstreamerRuntimeImpl(const Options& options) : options_(options) {}

  // Allocates and initializes a Gstreamer pipeline and any necessary resources
  // to support its execution.
  Status Initialize();

  // Feeds a GstreamerBuffer down the Gstreamer pipeline.
  Status Feed(const GstreamerBuffer&);

  // Frees the Gstreamer pipeline and any resources that was needed for its
  // executution.
  Status Finalize();

  ~GstreamerRuntimeImpl();

 private:
  Options options_;
  GstElement* gst_pipeline_ = nullptr;
  GMainLoop* glib_main_loop_ = nullptr;
  GstElement* gst_appsrc_ = nullptr;
  GstElement* gst_appsink_ = nullptr;
  std::thread glib_main_loop_runner_;
};

Status GstreamerRunner::GstreamerRuntimeImpl::Initialize() {
  // Create the full gstreamer pipeline.
  std::string pipeline_string =
      absl::StrFormat("appsrc name=%s ! %s ! appsink name=%s", kAppSrcName,
                      options_.processing_pipeline_string, kAppSinkName);
  gst_pipeline_ = gst_parse_launch(pipeline_string.c_str(), NULL);
  if (gst_pipeline_ == nullptr) {
    return InvalidArgumentError(absl::StrFormat(
        "Failed to create a gstreamer pipeline using \"%s\". Make sure you've "
        "given a valid processing pipeline string",
        pipeline_string));
  }

  // Create the GLib event loop.
  glib_main_loop_ = g_main_loop_new(NULL, FALSE);
  GstBus* bus = gst_element_get_bus(gst_pipeline_);
  gst_bus_add_watch(bus, (GstBusFunc)gst_bus_message_callback, glib_main_loop_);
  gst_object_unref(bus);

  // Setup the appsrc.
  gst_appsrc_ = gst_bin_get_by_name(GST_BIN(gst_pipeline_), kAppSrcName);
  if (gst_appsrc_ == nullptr) {
    return InternalError("Failed to get a pointer to the appsrc element");
  }
  GstCaps* appsrc_caps =
      gst_caps_from_string(options_.appsrc_caps_string.c_str());
  if (appsrc_caps == nullptr) {
    return InvalidArgumentError(absl::StrFormat(
        "Failed to create a GstCaps from \"%s\"; make sure it is a valid "
        "cap string",
        options_.appsrc_caps_string));
  }
  g_object_set(G_OBJECT(gst_appsrc_), "caps", appsrc_caps, NULL);
  gst_caps_unref(appsrc_caps);

  // Setup the appsink.
  gst_appsink_ = gst_bin_get_by_name(GST_BIN(gst_pipeline_), kAppSinkName);
  if (gst_appsink_ == nullptr) {
    return InternalError("Failed to get a pointer to the appsink element");
  }
  g_object_set(G_OBJECT(gst_appsink_), "emit-signals", TRUE, "sync", FALSE,
               NULL);
  g_signal_connect(gst_appsink_, "new-sample",
                   G_CALLBACK(on_new_sample_from_sink),
                   &options_.receiver_callback);

  // Play the gstreamer pipeline and start the glib main loop.
  gst_element_set_state(gst_pipeline_, GST_STATE_PLAYING);
  glib_main_loop_runner_ =
      std::thread([this]() { g_main_loop_run(glib_main_loop_); });

  return OkStatus();
}

Status GstreamerRunner::GstreamerRuntimeImpl::Finalize() {
  // Signal EOS. This will cause the pipeline to cease and trigger the GLib
  // event loop to stop.
  //
  // TODO(dschao): Check whether this works when the pipeline is already in an
  // error state. Find the "end-of-stream" handler in the gstreamer source.
  GstFlowReturn ret;
  g_signal_emit_by_name(gst_appsrc_, "end-of-stream", &ret);
  if (glib_main_loop_runner_.joinable()) {
    glib_main_loop_runner_.join();
  }

  // Set the pipeline to the null state.
  gst_element_set_state(gst_pipeline_, GST_STATE_NULL);

  // Cleanup.
  gst_object_unref(gst_appsink_);
  gst_object_unref(gst_appsrc_);
  g_main_loop_unref(glib_main_loop_);
  gst_object_unref(gst_pipeline_);

  return OkStatus();
}

Status GstreamerRunner::GstreamerRuntimeImpl::Feed(
    const GstreamerBuffer& gstreamer_buffer) {
  // Check that the given caps agree with those of the pipeline.
  //
  // Our way of supporting caps changes is to bringup/teardown the
  // GstreamerRuntimeImpl itself rather than deal with the nuances of doing this
  // in situ from within Gstreamer.
  if (gstreamer_buffer.get_caps() != options_.appsrc_caps_string) {
    return InvalidArgumentError(absl::StrFormat(
        "Feeding an appsrc with caps \"%s\" with a data of caps \"%s\"",
        options_.appsrc_caps_string, gstreamer_buffer.get_caps()));
  }

  // Create a new GstBuffer by copying.
  GstBuffer* buffer = gst_buffer_new_and_alloc(gstreamer_buffer.size());
  GstMapInfo map;
  gst_buffer_map(buffer, &map, GST_MAP_READWRITE);
  std::copy(gstreamer_buffer.data(),
            gstreamer_buffer.data() + gstreamer_buffer.size(), (char*)map.data);
  gst_buffer_unmap(buffer, &map);

  // Feed the buffer.
  GstFlowReturn ret;
  g_signal_emit_by_name(gst_appsrc_, "push-buffer", buffer, &ret);
  gst_buffer_unref(buffer);
  if (ret != GST_FLOW_OK) {
    return InternalError("Failed to push a GstBuffer");
  }
  return OkStatus();
}

GstreamerRunner::GstreamerRuntimeImpl::~GstreamerRuntimeImpl() {
  // Prevent the program from terminating if Finalize wasn't called/complete.
  if (glib_main_loop_runner_.joinable()) {
    glib_main_loop_runner_.detach();
  }
}

// -----------------------------------------------------------------------
// Begin GstreamerRunner implementation

GstreamerRunner::GstreamerRunner() = default;

GstreamerRunner::GstreamerRunner(const GstreamerRunnerOptions& options)
    : options_(options) {}

GstreamerRunner::~GstreamerRunner() {}

Status GstreamerRunner::SetOptions(const GstreamerRunnerOptions& options) {
  if (IsStarted()) {
    return FailedPreconditionError(
        "You cannot SetOptions while the runner has Started");
  }
  options_ = options;
  return OkStatus();
}

Status GstreamerRunner::SetReceiver(const ReceiverCallback& callback) {
  if (IsStarted()) {
    return FailedPreconditionError(
        "You cannot SetReceiver while the runner has Started");
  }
  receiver_callback_ = callback;
  return OkStatus();
}

bool GstreamerRunner::IsStarted() const {
  return gstreamer_runtime_impl_ != nullptr;
}

Status GstreamerRunner::Start() {
  if (IsStarted()) {
    return FailedPreconditionError("The runner has already Started");
  }

  // Initialize gstreamer if not already.
  Status status = GstInit();
  if (!status.ok()) {
    LOG(ERROR) << status;
    return InternalError("Could not initialize GStreamer");
  }

  // Create a GstreamerRuntimeImpl.
  status = ValidateRunnerOptions(options_);
  if (!status.ok()) {
    LOG(ERROR) << status;
    return InvalidArgumentError("The given GstreamerRunnerOptions has errors");
  }
  GstreamerRuntimeImpl::Options options;
  options.processing_pipeline_string = options_.processing_pipeline_string;
  options.appsrc_caps_string = options_.appsrc_caps_string;
  options.receiver_callback = receiver_callback_;
  gstreamer_runtime_impl_ = std::make_unique<GstreamerRuntimeImpl>(options);

  // Initialize the GstreamerRuntimeImpl.
  status = gstreamer_runtime_impl_->Initialize();
  if (!status.ok()) {
    LOG(ERROR) << status;

    // TODO(dschao): This could leak resources. Probably ok for most cases, but
    // would be nice if this could be friendlier.
    gstreamer_runtime_impl_.reset(nullptr);
    return UnknownError("Failed to Initialize a GstreamerRuntimeImpl");
  }

  return OkStatus();
}

Status GstreamerRunner::End() {
  if (!IsStarted()) {
    return FailedPreconditionError("The runner has already Ended");
  }
  Status status = gstreamer_runtime_impl_->Finalize();
  gstreamer_runtime_impl_.reset(nullptr);
  if (!status.ok()) {
    return UnknownError("Failed to Finalize a GstreamerRuntimeImpl");
  }
  return OkStatus();
}

Status GstreamerRunner::Feed(const GstreamerBuffer& gstreamer_buffer) {
  if (!IsStarted()) {
    return FailedPreconditionError("The runner has not been Started");
  }
  Status status = gstreamer_runtime_impl_->Feed(gstreamer_buffer);
  if (!status.ok()) {
    LOG(ERROR) << status;
    return UnknownError("Failed to Feed the GstreamerRunner");
  }
  return OkStatus();
}

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

#include "aistreams/gstreamer/gstreamer_runner.h"

#include <gst/app/gstappsink.h>
#include <gst/gst.h>

#include <algorithm>
#include <thread>
#include <utility>

#include "absl/strings/str_format.h"
#include "aistreams/gstreamer/gstreamer_utils.h"
#include "aistreams/port/canonical_errors.h"
#include "aistreams/port/logging.h"
#include "aistreams/port/status.h"
#include "aistreams/port/status_macros.h"

namespace aistreams {

namespace {

constexpr char kAppSrcName[] = "feed";
constexpr char kAppSinkName[] = "fetch";

// Callback attached to the GLib main loop.
gboolean gst_bus_message_callback(GstBus* bus, GstMessage* message,
                                  GMainLoop* loop) {
  GError* err;
  gchar* debug_info;
  switch (GST_MESSAGE_TYPE(message)) {
    case GST_MESSAGE_EOS:
      g_main_loop_quit(loop);
      break;
    case GST_MESSAGE_ERROR:
      gst_message_parse_error(message, &err, &debug_info);
      LOG(ERROR) << absl::StrFormat("Error from gstreamer element %s: %s",
                                    GST_OBJECT_NAME(message->src),
                                    err->message);
      LOG(ERROR) << absl::StrFormat("Additional debug info: %s",
                                    debug_info ? debug_info : "none");
      LOG(ERROR) << "Got gstreamer error; shutting down event loop";
      g_main_loop_quit(loop);
      break;
    default:
      break;
  }
  return TRUE;
}

// Callback for receiving new GstSample's from appsink.
GstFlowReturn on_new_sample_from_sink(
    GstElement* elt, GstreamerRunner::ReceiverCallback* receiver_callback) {
  // Get the GstSample from appsink.
  GstSample* sample = gst_app_sink_pull_sample(GST_APP_SINK(elt));

  // No-op if callbacks are not supplied.
  if (receiver_callback == nullptr || !(*receiver_callback)) {
    gst_sample_unref(sample);
    return GST_FLOW_OK;
  }

  // Copy the GstSample into aistreamer's GstreamerBuffer type.
  GstreamerBuffer gstreamer_buffer;

  GstCaps* caps = gst_sample_get_caps(sample);
  gchar* caps_string = gst_caps_to_string(caps);
  gstreamer_buffer.set_caps_string(caps_string);
  g_free(caps_string);

  GstBuffer* buffer = gst_sample_get_buffer(sample);
  GstMapInfo map;
  gst_buffer_map(buffer, &map, GST_MAP_READ);
  gstreamer_buffer.assign(reinterpret_cast<char*>(map.data), map.size);
  gst_buffer_unmap(buffer, &map);
  gst_sample_unref(sample);

  // Deliver the GstreamerBuffer using the callback.
  // TODO: Decide on special status codes to pause/halt the pipeline.
  Status status = (*receiver_callback)(std::move(gstreamer_buffer));
  if (!status.ok()) {
    LOG(ERROR) << status.message();
  }
  return GST_FLOW_OK;
}

Status ValidateRunnerOptions(const GstreamerRunnerOptions& options) {
  if (options.processing_pipeline_string.empty()) {
    return InvalidArgumentError("Given an empty processing pipeline string");
  }
  if (options.appsrc_caps_string.empty()) {
    return InvalidArgumentError("Given an empty appsrc caps string");
  }
  return OkStatus();
}

}  // namespace

// A class that manages the runtime/context of a Gstreamer pipeline.
class GstreamerRunner::GstreamerRuntimeImpl {
 public:
  struct Options {
    std::string processing_pipeline_string;
    std::string appsrc_caps_string;
    ReceiverCallback receiver_callback;
  };

  GstreamerRuntimeImpl(const Options& options) : options_(options) {}

  // Allocates and initializes a Gstreamer pipeline and any necessary resources
  // to support its execution.
  Status Initialize();

  // Feeds a GstreamerBuffer down the Gstreamer pipeline.
  Status Feed(const GstreamerBuffer&);

  // Frees the Gstreamer pipeline and any resources that was needed for its
  // executution.
  Status Finalize();

  ~GstreamerRuntimeImpl();

 private:
  Options options_;
  GstElement* gst_pipeline_ = nullptr;
  GMainLoop* glib_main_loop_ = nullptr;
  GstElement* gst_appsrc_ = nullptr;
  GstElement* gst_appsink_ = nullptr;
  std::thread glib_main_loop_runner_;
};

Status GstreamerRunner::GstreamerRuntimeImpl::Initialize() {
  // Create the full gstreamer pipeline.
  std::string pipeline_string = absl::StrFormat(
      "appsrc name=%s is-live=true ! %s ! appsink name=%s", kAppSrcName,
      options_.processing_pipeline_string, kAppSinkName);
  gst_pipeline_ = gst_parse_launch(pipeline_string.c_str(), NULL);
  if (gst_pipeline_ == nullptr) {
    return InvalidArgumentError(absl::StrFormat(
        "Failed to create a gstreamer pipeline using \"%s\". Make sure you've "
        "given a valid processing pipeline string",
        pipeline_string));
  }

  // Create the GLib event loop.
  glib_main_loop_ = g_main_loop_new(NULL, FALSE);
  GstBus* bus = gst_element_get_bus(gst_pipeline_);
  gst_bus_add_watch(bus, (GstBusFunc)gst_bus_message_callback, glib_main_loop_);
  gst_object_unref(bus);

  // Setup the appsrc.
  gst_appsrc_ = gst_bin_get_by_name(GST_BIN(gst_pipeline_), kAppSrcName);
  if (gst_appsrc_ == nullptr) {
    return InternalError("Failed to get a pointer to the appsrc element");
  }
  GstCaps* appsrc_caps =
      gst_caps_from_string(options_.appsrc_caps_string.c_str());
  if (appsrc_caps == nullptr) {
    return InvalidArgumentError(absl::StrFormat(
        "Failed to create a GstCaps from \"%s\"; make sure it is a valid "
        "cap string",
        options_.appsrc_caps_string));
  }
  g_object_set(G_OBJECT(gst_appsrc_), "caps", appsrc_caps, NULL);
  gst_caps_unref(appsrc_caps);

  // Setup the appsink.
  gst_appsink_ = gst_bin_get_by_name(GST_BIN(gst_pipeline_), kAppSinkName);
  if (gst_appsink_ == nullptr) {
    return InternalError("Failed to get a pointer to the appsink element");
  }
  g_object_set(G_OBJECT(gst_appsink_), "emit-signals", TRUE, "sync", FALSE,
               NULL);
  g_signal_connect(gst_appsink_, "new-sample",
                   G_CALLBACK(on_new_sample_from_sink),
                   &options_.receiver_callback);

  // Play the gstreamer pipeline and start the glib main loop.
  gst_element_set_state(gst_pipeline_, GST_STATE_PLAYING);
  glib_main_loop_runner_ =
      std::thread([this]() { g_main_loop_run(glib_main_loop_); });

  return OkStatus();
}

Status GstreamerRunner::GstreamerRuntimeImpl::Finalize() {
  // Signal EOS. This will cause the pipeline to cease and trigger the GLib
  // event loop to stop.
  //
  // TODO(dschao): Check whether this works when the pipeline is already in an
  // error state. Find the "end-of-stream" handler in the gstreamer source.
  GstFlowReturn ret;
  g_signal_emit_by_name(gst_appsrc_, "end-of-stream", &ret);
  if (glib_main_loop_runner_.joinable()) {
    glib_main_loop_runner_.join();
  }

  // Set the pipeline to the null state.
  gst_element_set_state(gst_pipeline_, GST_STATE_NULL);

  // Cleanup.
  gst_object_unref(gst_appsink_);
  gst_object_unref(gst_appsrc_);
  g_main_loop_unref(glib_main_loop_);
  gst_object_unref(gst_pipeline_);

  return OkStatus();
}

Status GstreamerRunner::GstreamerRuntimeImpl::Feed(
    const GstreamerBuffer& gstreamer_buffer) {
  // Check that the given caps agree with those of the pipeline.
  //
  // Our way of supporting caps changes is to bringup/teardown the
  // GstreamerRuntimeImpl itself rather than deal with the nuances of doing this
  // in situ from within Gstreamer.
  if (gstreamer_buffer.get_caps() != options_.appsrc_caps_string) {
    return InvalidArgumentError(absl::StrFormat(
        "Feeding an appsrc with caps \"%s\" with a data of caps \"%s\"",
        options_.appsrc_caps_string, gstreamer_buffer.get_caps()));
  }

  // Create a new GstBuffer by copying.
  GstBuffer* buffer = gst_buffer_new_and_alloc(gstreamer_buffer.size());
  GstMapInfo map;
  gst_buffer_map(buffer, &map, GST_MAP_READWRITE);
  std::copy(gstreamer_buffer.data(),
            gstreamer_buffer.data() + gstreamer_buffer.size(), (char*)map.data);
  gst_buffer_unmap(buffer, &map);

  // Feed the buffer.
  GstFlowReturn ret;
  g_signal_emit_by_name(gst_appsrc_, "push-buffer", buffer, &ret);
  gst_buffer_unref(buffer);
  if (ret != GST_FLOW_OK) {
    return InternalError("Failed to push a GstBuffer");
  }
  return OkStatus();
}

GstreamerRunner::GstreamerRuntimeImpl::~GstreamerRuntimeImpl() {
  // Prevent the program from terminating if Finalize wasn't called/complete.
  if (glib_main_loop_runner_.joinable()) {
    glib_main_loop_runner_.detach();
  }
}

// -----------------------------------------------------------------------
// Begin GstreamerRunner implementation

GstreamerRunner::GstreamerRunner() = default;

GstreamerRunner::GstreamerRunner(const GstreamerRunnerOptions& options)
    : options_(options) {}

GstreamerRunner::~GstreamerRunner() {}

Status GstreamerRunner::SetOptions(const GstreamerRunnerOptions& options) {
  if (IsStarted()) {
    return FailedPreconditionError(
        "You cannot SetOptions while the runner has Started");
  }
  options_ = options;
  return OkStatus();
}

Status GstreamerRunner::SetReceiver(const ReceiverCallback& callback) {
  if (IsStarted()) {
    return FailedPreconditionError(
        "You cannot SetReceiver while the runner has Started");
  }
  receiver_callback_ = callback;
  return OkStatus();
}

bool GstreamerRunner::IsStarted() const {
  return gstreamer_runtime_impl_ != nullptr;
}

Status GstreamerRunner::Start() {
  if (IsStarted()) {
    return FailedPreconditionError("The runner has already Started");
  }

  // Initialize gstreamer if not already.
  Status status = GstInit();
  if (!status.ok()) {
    LOG(ERROR) << status;
    return InternalError("Could not initialize GStreamer");
  }

  // Create a GstreamerRuntimeImpl.
  status = ValidateRunnerOptions(options_);
  if (!status.ok()) {
    LOG(ERROR) << status;
    return InvalidArgumentError("The given GstreamerRunnerOptions has errors");
  }
  GstreamerRuntimeImpl::Options options;
  options.processing_pipeline_string = options_.processing_pipeline_string;
  options.appsrc_caps_string = options_.appsrc_caps_string;
  options.receiver_callback = receiver_callback_;
  gstreamer_runtime_impl_ = std::make_unique<GstreamerRuntimeImpl>(options);

  // Initialize the GstreamerRuntimeImpl.
  status = gstreamer_runtime_impl_->Initialize();
  if (!status.ok()) {
    LOG(ERROR) << status;

    // TODO(dschao): This could leak resources. Probably ok for most cases, but
    // would be nice if this could be friendlier.
    gstreamer_runtime_impl_.reset(nullptr);
    return UnknownError("Failed to Initialize a GstreamerRuntimeImpl");
  }

  return OkStatus();
}

Status GstreamerRunner::End() {
  if (!IsStarted()) {
    return FailedPreconditionError("The runner has already Ended");
  }
  Status status = gstreamer_runtime_impl_->Finalize();
  gstreamer_runtime_impl_.reset(nullptr);
  if (!status.ok()) {
    return UnknownError("Failed to Finalize a GstreamerRuntimeImpl");
  }
  return OkStatus();
}

Status GstreamerRunner::Feed(const GstreamerBuffer& gstreamer_buffer) {
  if (!IsStarted()) {
    return FailedPreconditionError("The runner has not been Started");
  }
  Status status = gstreamer_runtime_impl_->Feed(gstreamer_buffer);
  if (!status.ok()) {
    LOG(ERROR) << status;
    return UnknownError("Failed to Feed the GstreamerRunner");
  }
  return OkStatus();
}

}  // namespace aistreams
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#include <gtest/gtest.h>
#include <uavcan/helpers/heap_based_pool_allocator.hpp>

inline bool atomicCompareAndSwap(void** address, void* expected, void* replacement)
{
    return __sync_bool_compare_and_swap(address, expected, replacement);
}

TEST(ThreadSafeHeapBasedPoolAllocator, Basic)
{
    uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, atomicCompareAndSwap> allocator;

    ASSERT_EQ(0, allocator.getNumCachedBlocks());

    void* a = allocator.allocate(10);
    void* b = allocator.allocate(10);
    void* c = allocator.allocate(10);
    void* d = allocator.allocate(10);

    ASSERT_EQ(0, allocator.getNumCachedBlocks());

    allocator.deallocate(a);
    ASSERT_EQ(1, allocator.getNumCachedBlocks());

    allocator.deallocate(b);
    ASSERT_EQ(2, allocator.getNumCachedBlocks());

    allocator.deallocate(c);
    ASSERT_EQ(3, allocator.getNumCachedBlocks());

    a = allocator.allocate(10);
    ASSERT_EQ(2, allocator.getNumCachedBlocks());
    ASSERT_EQ(c, a);

    allocator.deallocate(a);
    ASSERT_EQ(3, allocator.getNumCachedBlocks());

    allocator.shrink();
    ASSERT_EQ(0, allocator.getNumCachedBlocks());

    allocator.deallocate(d);
    ASSERT_EQ(1, allocator.getNumCachedBlocks());

    allocator.shrink();
    ASSERT_EQ(0, allocator.getNumCachedBlocks());
}
<commit_msg>Some tests for HeapBasedPoolAllocator<>; the concurrency test is failing with heap corruption<commit_after>/*
 * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#include <gtest/gtest.h>
#include <uavcan/helpers/heap_based_pool_allocator.hpp>
#include <malloc.h>

inline bool atomicCompareAndSwap(void** address, void* expected, void* replacement)
{
    return __sync_bool_compare_and_swap(address, expected, replacement);
}

TEST(HeapBasedPoolAllocator, Basic)
{
    std::cout << ">>> HEAP BEFORE:" << std::endl;
    malloc_stats();

    uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, atomicCompareAndSwap> al;

    ASSERT_EQ(0, al.getNumCachedBlocks());

    void* a = al.allocate(10);
    void* b = al.allocate(10);
    void* c = al.allocate(10);
    void* d = al.allocate(10);

    ASSERT_EQ(0, al.getNumCachedBlocks());

    al.deallocate(a);
    ASSERT_EQ(1, al.getNumCachedBlocks());

    al.deallocate(b);
    ASSERT_EQ(2, al.getNumCachedBlocks());

    al.deallocate(c);
    ASSERT_EQ(3, al.getNumCachedBlocks());

    a = al.allocate(10);
    ASSERT_EQ(2, al.getNumCachedBlocks());
    ASSERT_EQ(c, a);

    al.deallocate(a);
    ASSERT_EQ(3, al.getNumCachedBlocks());

    al.shrink();
    ASSERT_EQ(0, al.getNumCachedBlocks());

    al.deallocate(d);
    ASSERT_EQ(1, al.getNumCachedBlocks());

    al.shrink();
    ASSERT_EQ(0, al.getNumCachedBlocks());

    std::cout << ">>> HEAP AFTER:" << std::endl;
    malloc_stats();
}

#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11

#include <thread>

inline bool atomicCompareAndSwapWithRescheduling(void** address, void* expected, void* replacement)
{
    /*
     * Yield is added for testing reasons, making CAS failure more likely
     */
    if ((float(std::rand()) / float(RAND_MAX)) < 0.05)
    {
        std::this_thread::yield();
    }
    return __sync_bool_compare_and_swap(address, expected, replacement);
}

TEST(HeapBasedPoolAllocator, Concurrency)
{
    std::cout << ">>> HEAP BEFORE:" << std::endl;
    malloc_stats();

    uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, atomicCompareAndSwapWithRescheduling> al;

    volatile bool terminate = false;

    /*
     * Starting the testing threads
     */
    std::thread threads[3];

    for (auto& x : threads)
    {
        x = std::thread([&al, &terminate]()
        {
            while (!terminate)
            {
                auto a = al.allocate(1);
                auto b = al.allocate(1);
                auto c = al.allocate(1);
                al.deallocate(al.allocate(1));
                al.deallocate(a);
                al.deallocate(b);
                al.deallocate(c);
            }
        });
    }

    /*
     * Running the threads for some time, then terminating
     */
    std::this_thread::sleep_for(std::chrono::seconds(1));

    terminate = true;
    std::cout << "Terminating workers..." << std::endl;

    for (auto& x : threads)
    {
        x.join();
    }
    std::cout << "All workers joined" << std::endl;

    /*
     * Now, there must not be any leaked memory, because the worker threads deallocate everything before completion.
     */
    //std::cout << "Cached blocks: " << al.getNumCachedBlocks() << std::endl;

    std::cout << ">>> HEAP BEFORE SHRINK:" << std::endl;
    malloc_stats();

    al.shrink();

    std::cout << ">>> HEAP AFTER SHRINK:" << std::endl;
    malloc_stats();
}

#endif
<|endoftext|>


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

#include "geocode.h"
#include <QDebug>
#include <QStringList>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QQmlInfo>
#include <QXmlStreamReader>

Geocode::Geocode(QObject *parent) :
  QObject(parent),
  m_manager(0),
  m_reply(0) {

}

Geocode::~Geocode() {
  setActive(false);
}

bool Geocode::isActive() const {
  return m_manager != 0;
}

void Geocode::setActive(bool active) {
  if (active == isActive()) {
    return;
  }

  if (active) {
    m_manager = new QNetworkAccessManager(this);
  } else {
    m_manager->deleteLater();
    m_manager = 0;

    if (m_reply) {
      m_reply->deleteLater();
      m_reply = 0;
    }
  }

  if (!active) {
    clear();
  }

  emit activeChanged();
}

QString Geocode::country() const {
  return m_country;
}

QString Geocode::city() const {
  return m_city;
}

QString Geocode::suburb() const {
  return m_suburb;
}

void Geocode::search(double longitude, double latitude) {
  if (!isActive()) {
    return;
  }

  if (m_reply) {
    m_reply->abort();
    delete m_reply;
    m_reply = 0;
  }

  QUrl url(QStringLiteral("http://nominatim.openstreetmap.org/reverse"));
  QUrlQuery query;
  query.addQueryItem(QStringLiteral("format"), QStringLiteral("xml"));
  query.addQueryItem(QStringLiteral("accept-language"), QStringLiteral("en"));
  query.addQueryItem(QStringLiteral("lat"), QString::number(latitude));
  query.addQueryItem(QStringLiteral("lon"), QString::number(longitude));
  query.addQueryItem(QStringLiteral("zoom"), QStringLiteral("18"));
  query.addQueryItem(QStringLiteral("addressdetails"), QStringLiteral("1"));
  url.setQuery(query);

  m_reply = m_manager->get(QNetworkRequest(url));
  QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished()));
}

void Geocode::clear() {
  if (m_reply) {
    m_reply->deleteLater();
    m_reply = 0;
  }

  if (!m_country.isEmpty()) {
    m_country.clear();
    emit countryChanged();
  }

  if (!m_city.isEmpty()) {
    m_city.clear();
    emit cityChanged();
  }

  if (!m_suburb.isEmpty()) {
    m_suburb.clear();
    emit suburbChanged();
  }
}

void Geocode::replyFinished() {
  QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
  if (!reply || reply != m_reply) {
    return;
  }

  if (reply->error() != QNetworkReply::NoError) {
    qmlInfo(this) << "Error while geocoding" << reply->errorString();

    reply->deleteLater();

    // Network related error. We will disable ourselves for now.
    setActive(false);

    m_reply = 0;
    clear();

    return;
  }

  QXmlStreamReader reader(m_reply);
  while (!reader.atEnd()) {
    reader.readNext();
    if (reader.isStartElement()) {
      QStringRef name = reader.name();
      if (name == "suburb") {
	QString suburb = reader.readElementText();
	if (m_suburb != suburb) {
	  m_suburb = suburb;
	  emit suburbChanged();
	}
      } else if (name == "city") {
	QString city = reader.readElementText();
	if (m_city != city) {
	  m_city = city;
	  emit cityChanged();
	}
      } else if (name == "country") {
	QString country = reader.readElementText();
	if (m_country != country) {
	  m_country = country;
	  emit countryChanged();
	}
      }
    }
  }

  m_reply->deleteLater();
  m_reply = 0;
}
<commit_msg>sailfish: don't deactivate geocoding if we have canceled a reply<commit_after>/*!
 * This file is part of CameraPlus.
 *
 * Copyright (C) 2012-2014 Mohammed Sameer <msameer@foolab.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "geocode.h"
#include <QDebug>
#include <QStringList>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QQmlInfo>
#include <QXmlStreamReader>

Geocode::Geocode(QObject *parent) :
  QObject(parent),
  m_manager(0),
  m_reply(0) {

}

Geocode::~Geocode() {
  setActive(false);
}

bool Geocode::isActive() const {
  return m_manager != 0;
}

void Geocode::setActive(bool active) {
  if (active == isActive()) {
    return;
  }

  if (active) {
    m_manager = new QNetworkAccessManager(this);
  } else {
    m_manager->deleteLater();
    m_manager = 0;

    if (m_reply) {
      m_reply->deleteLater();
      m_reply = 0;
    }
  }

  if (!active) {
    clear();
  }

  emit activeChanged();
}

QString Geocode::country() const {
  return m_country;
}

QString Geocode::city() const {
  return m_city;
}

QString Geocode::suburb() const {
  return m_suburb;
}

void Geocode::search(double longitude, double latitude) {
  if (!isActive()) {
    return;
  }

  if (m_reply) {
    m_reply->abort();
    delete m_reply;
    m_reply = 0;
  }

  QUrl url(QStringLiteral("http://nominatim.openstreetmap.org/reverse"));
  QUrlQuery query;
  query.addQueryItem(QStringLiteral("format"), QStringLiteral("xml"));
  query.addQueryItem(QStringLiteral("accept-language"), QStringLiteral("en"));
  query.addQueryItem(QStringLiteral("lat"), QString::number(latitude));
  query.addQueryItem(QStringLiteral("lon"), QString::number(longitude));
  query.addQueryItem(QStringLiteral("zoom"), QStringLiteral("18"));
  query.addQueryItem(QStringLiteral("addressdetails"), QStringLiteral("1"));
  url.setQuery(query);

  m_reply = m_manager->get(QNetworkRequest(url));
  QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished()));
}

void Geocode::clear() {
  if (m_reply) {
    m_reply->deleteLater();
    m_reply = 0;
  }

  if (!m_country.isEmpty()) {
    m_country.clear();
    emit countryChanged();
  }

  if (!m_city.isEmpty()) {
    m_city.clear();
    emit cityChanged();
  }

  if (!m_suburb.isEmpty()) {
    m_suburb.clear();
    emit suburbChanged();
  }
}

void Geocode::replyFinished() {
  QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
  if (!reply || reply != m_reply) {
    return;
  }

  if (reply->error() != QNetworkReply::NoError) {
    qmlInfo(this) << "Error while geocoding " << reply->errorString();

    // Network related error. We will disable ourselves for now.
    if (reply->error() != QNetworkReply::OperationCanceledError) {
      setActive(false);
    }

    reply->deleteLater();

    m_reply = 0;
    clear();

    return;
  }

  QXmlStreamReader reader(m_reply);
  while (!reader.atEnd()) {
    reader.readNext();
    if (reader.isStartElement()) {
      QStringRef name = reader.name();
      if (name == "suburb") {
	QString suburb = reader.readElementText();
	if (m_suburb != suburb) {
	  m_suburb = suburb;
	  emit suburbChanged();
	}
      } else if (name == "city") {
	QString city = reader.readElementText();
	if (m_city != city) {
	  m_city = city;
	  emit cityChanged();
	}
      } else if (name == "country") {
	QString country = reader.readElementText();
	if (m_country != country) {
	  m_country = country;
	  emit countryChanged();
	}
      }
    }
  }

  m_reply->deleteLater();
  m_reply = 0;
}
<|endoftext|>


<commit_before>#include <cassert>
#include <stdexcept>
#include <unordered_map>

#include "extension.h"
#include "extension_p.h"

#ifdef ZTS
#include <mutex>
#endif

ZEND_DECLARE_MODULE_GLOBALS(phpcxx);

class ExtensionMap {
private:
#ifdef ZTS
    std::mutex                                          g_mutex;        ///< Mutex to synchronize access to the maps
#endif
    std::unordered_map<std::string, phpcxx::Extension*> g_name2ext;     ///< Maps extension name to Extension
    std::unordered_map<int, phpcxx::Extension*>         g_id2ext;       ///< Maps extension number to Extension

    ExtensionMap() {}
    ExtensionMap(const ExtensionMap&) = delete;
    ExtensionMap& operator=(const ExtensionMap&) = delete;

public:
    phpcxx::Extension* extByModNumber(int module)
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif

        auto i2e_it = this->g_id2ext.find(module);
        if (EXPECTED(i2e_it != this->g_id2ext.end())) {
            return i2e_it->second;
        }

        return nullptr;
    }

    bool extensionExists(const char* name)
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif
        return this->g_name2ext.count(std::string(name)) != 0;
    }

    void addExtension(const char* name, phpcxx::Extension* ext)
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif
        this->g_name2ext.emplace(std::string(name), ext);
    }

    phpcxx::Extension* mapNumberToExt(const char* name, int number)
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif

        auto n2e_it = this->g_name2ext.find(name);
        if (EXPECTED(n2e_it != this->g_name2ext.end())) {
            phpcxx::Extension* e = n2e_it->second;
            this->g_id2ext.emplace(number, e);
            // this->g_name2ext.erase(n2e_it);
            return e;
        }

        return nullptr;
    }

    /**
     * Ensures that ExtensionMap is initialized when we call this function.
     * If we declare ExtensionMap as a global static, we will depend on the
     * initialization order of static globals, which, in turn, depends on
     * the order of object files during the link time. With static
     * ExtensionMap we encountered strange floating point exceptions
     * happening inside std::unordered_map - possibly because emplace()
     * happened before the map was really initialized.
     */
    static ExtensionMap& instance()
    {
        static ExtensionMap self;
        return self;
    }
};

static void complainExtensionNotFound(int number)
{
    zend_error(
        E_WARNING,
        "Unable to find the module by number %d. Probably phpcxx::Extension instance has been destroyed too early",
        number
    );
}

phpcxx::ExtensionPrivate::ExtensionPrivate(phpcxx::Extension* const q, const char* name, const char* version)
    : q_ptr(q)
{
    if (!name) {
        throw std::logic_error("Extension name not specified");
    }

    if (ExtensionMap::instance().extensionExists(name)) {
        throw std::logic_error("This extension has already been registered - " + std::string(name));
    }

    this->entry.name    = name;
    this->entry.version = version;

    ExtensionMap::instance().addExtension(name, q);
}

int phpcxx::ExtensionPrivate::moduleStartup(INIT_FUNC_ARGS)
{
    zend_module_entry* me = EG(current_module);
    assert(me != nullptr);

    phpcxx::Extension* e = ExtensionMap::instance().mapNumberToExt(me->name, module_number);

//  const zend_ini_entry_def* ini;
//  zend_register_ini_entries(ini, int module_number)

    if (EXPECTED(e)) {
        try {
            e->onModuleStartup();
            return SUCCESS;
        }
        catch (const std::exception& e) {
            zend_error(E_ERROR, "%s", e.what());
            return FAILURE;
        }
    }

    complainExtensionNotFound(module_number);
    return SUCCESS;
}

int phpcxx::ExtensionPrivate::moduleShutdown(SHUTDOWN_FUNC_ARGS)
{
    zend_unregister_ini_entries(module_number);

    phpcxx::Extension* e = ExtensionMap::instance().extByModNumber(module_number);
    if (EXPECTED(e)) {
        try {
            e->onModuleShutdown();
            return SUCCESS;
        }
        catch (const std::exception& e) {
            zend_error(E_ERROR, "%s", e.what());
            return FAILURE;
        }
    }

    complainExtensionNotFound(module_number);
    return SUCCESS;
}

int phpcxx::ExtensionPrivate::requestStartup(INIT_FUNC_ARGS)
{
    phpcxx::Extension* e = ExtensionMap::instance().extByModNumber(module_number);
    if (EXPECTED(e)) {
        try {
            e->onRequestStartup();
            return SUCCESS;
        }
        catch (const std::exception& e) {
            zend_error(E_ERROR, "%s", e.what());
            return FAILURE;
        }
    }

    complainExtensionNotFound(module_number);
    return SUCCESS;
}

int phpcxx::ExtensionPrivate::requestShutdown(SHUTDOWN_FUNC_ARGS)
{
    phpcxx::Extension* e = ExtensionMap::instance().extByModNumber(module_number);
    if (EXPECTED(e)) {
        try {
            e->onRequestShutdown();
            return SUCCESS;
        }
        catch (const std::exception& e) {
            zend_error(E_ERROR, "%s", e.what());
            return FAILURE;
        }
    }

    complainExtensionNotFound(module_number);
    return SUCCESS;
}

void phpcxx::ExtensionPrivate::moduleInfo(ZEND_MODULE_INFO_FUNC_ARGS)
{
    phpcxx::Extension* e = ExtensionMap::instance().extByModNumber(zend_module->module_number);
    if (EXPECTED(e)) {
        e->onModuleInfo();
    }
    else {
        complainExtensionNotFound(zend_module->module_number);
    }
}

void phpcxx::ExtensionPrivate::globalsInit(void* g)
{
}

void phpcxx::ExtensionPrivate::globalsShutdown(void* g)
{
}

void phpcxx::ExtensionPrivate::doRegisterFunction(const Function& f)
{
    std::unique_ptr<ExtensionPrivate::LateFunction> late(new ExtensionPrivate::LateFunction);

    late->fe = f.getFE();
    late->ai = f.getArgInfo();

    zend_register_functions(nullptr, &late->fe, nullptr, MODULE_PERSISTENT);
    this->m_late_functions.push_back(std::move(late));
}
<commit_msg>Clear maps on GSHUTDOWN<commit_after>#include <cassert>
#include <stdexcept>
#include <unordered_map>

#include "extension.h"
#include "extension_p.h"

#ifdef ZTS
#include <mutex>
#endif

ZEND_DECLARE_MODULE_GLOBALS(phpcxx);

class ExtensionMap {
private:
#ifdef ZTS
    std::mutex                                          g_mutex;        ///< Mutex to synchronize access to the maps
#endif
    std::unordered_map<std::string, phpcxx::Extension*> g_name2ext;     ///< Maps extension name to Extension
    std::unordered_map<int, phpcxx::Extension*>         g_id2ext;       ///< Maps extension number to Extension

    ExtensionMap() {}
    ExtensionMap(const ExtensionMap&) = delete;
    ExtensionMap& operator=(const ExtensionMap&) = delete;

public:
    phpcxx::Extension* extByModNumber(int module)
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif

        auto i2e_it = this->g_id2ext.find(module);
        if (EXPECTED(i2e_it != this->g_id2ext.end())) {
            return i2e_it->second;
        }

        return nullptr;
    }

    bool extensionExists(const char* name)
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif
        return this->g_name2ext.count(std::string(name)) != 0;
    }

    void addExtension(const char* name, phpcxx::Extension* ext)
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif
        this->g_name2ext.emplace(std::string(name), ext);
    }

    phpcxx::Extension* mapNumberToExt(const char* name, int number)
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif

        auto n2e_it = this->g_name2ext.find(name);
        if (EXPECTED(n2e_it != this->g_name2ext.end())) {
            phpcxx::Extension* e = n2e_it->second;
            this->g_id2ext.emplace(number, e);
            // this->g_name2ext.erase(n2e_it);
            return e;
        }

        return nullptr;
    }

    void clear()
    {
#ifdef ZTS
        std::unique_lock<std::mutex> locker(this->g_mutex);
#endif
        this->g_id2ext.clear();
        this->g_name2ext.clear();
    }

    /**
     * Ensures that ExtensionMap is initialized when we call this function.
     * If we declare ExtensionMap as a global static, we will depend on the
     * initialization order of static globals, which, in turn, depends on
     * the order of object files during the link time. With static
     * ExtensionMap we encountered strange floating point exceptions
     * happening inside std::unordered_map - possibly because emplace()
     * happened before the map was really initialized.
     */
    static ExtensionMap& instance()
    {
        static ExtensionMap self;
        return self;
    }
};

static void complainExtensionNotFound(int number)
{
    zend_error(
        E_WARNING,
        "Unable to find the module by number %d. Probably phpcxx::Extension instance has been destroyed too early",
        number
    );
}

phpcxx::ExtensionPrivate::ExtensionPrivate(phpcxx::Extension* const q, const char* name, const char* version)
    : q_ptr(q)
{
    if (!name) {
        throw std::logic_error("Extension name not specified");
    }

    if (ExtensionMap::instance().extensionExists(name)) {
        throw std::logic_error("This extension has already been registered - " + std::string(name));
    }

    this->entry.name    = name;
    this->entry.version = version;

    ExtensionMap::instance().addExtension(name, q);
}

int phpcxx::ExtensionPrivate::moduleStartup(INIT_FUNC_ARGS)
{
    zend_module_entry* me = EG(current_module);
    assert(me != nullptr);

    phpcxx::Extension* e = ExtensionMap::instance().mapNumberToExt(me->name, module_number);

//  const zend_ini_entry_def* ini;
//  zend_register_ini_entries(ini, int module_number)

    if (EXPECTED(e)) {
        try {
            e->onModuleStartup();
            return SUCCESS;
        }
        catch (const std::exception& e) {
            zend_error(E_ERROR, "%s", e.what());
            return FAILURE;
        }
    }

    complainExtensionNotFound(module_number);
    return SUCCESS;
}

int phpcxx::ExtensionPrivate::moduleShutdown(SHUTDOWN_FUNC_ARGS)
{
    zend_unregister_ini_entries(module_number);

    phpcxx::Extension* e = ExtensionMap::instance().extByModNumber(module_number);
    if (EXPECTED(e)) {
        try {
            e->onModuleShutdown();
            return SUCCESS;
        }
        catch (const std::exception& e) {
            zend_error(E_ERROR, "%s", e.what());
            return FAILURE;
        }
    }

    complainExtensionNotFound(module_number);
    return SUCCESS;
}

int phpcxx::ExtensionPrivate::requestStartup(INIT_FUNC_ARGS)
{
    phpcxx::Extension* e = ExtensionMap::instance().extByModNumber(module_number);
    if (EXPECTED(e)) {
        try {
            e->onRequestStartup();
            return SUCCESS;
        }
        catch (const std::exception& e) {
            zend_error(E_ERROR, "%s", e.what());
            return FAILURE;
        }
    }

    complainExtensionNotFound(module_number);
    return SUCCESS;
}

int phpcxx::ExtensionPrivate::requestShutdown(SHUTDOWN_FUNC_ARGS)
{
    phpcxx::Extension* e = ExtensionMap::instance().extByModNumber(module_number);
    if (EXPECTED(e)) {
        try {
            e->onRequestShutdown();
            return SUCCESS;
        }
        catch (const std::exception& e) {
            zend_error(E_ERROR, "%s", e.what());
            return FAILURE;
        }
    }

    complainExtensionNotFound(module_number);
    return SUCCESS;
}

void phpcxx::ExtensionPrivate::moduleInfo(ZEND_MODULE_INFO_FUNC_ARGS)
{
    phpcxx::Extension* e = ExtensionMap::instance().extByModNumber(zend_module->module_number);
    if (EXPECTED(e)) {
        e->onModuleInfo();
    }
    else {
        complainExtensionNotFound(zend_module->module_number);
    }
}

void phpcxx::ExtensionPrivate::globalsInit(void* g)
{
}

void phpcxx::ExtensionPrivate::globalsShutdown(void* g)
{
    ExtensionMap::instance().clear();
}

void phpcxx::ExtensionPrivate::doRegisterFunction(const Function& f)
{
    std::unique_ptr<ExtensionPrivate::LateFunction> late(new ExtensionPrivate::LateFunction);

    late->fe = f.getFE();
    late->ai = f.getArgInfo();

    zend_register_functions(nullptr, &late->fe, nullptr, MODULE_PERSISTENT);
    this->m_late_functions.push_back(std::move(late));
}
<|endoftext|>


<commit_before>//---------------------------------------------------------------------------//
//!
//! \file   BasicParticleState.hpp
//! \author Alex Robinson
//! \brief  Basic particle state class declaration
//!
//---------------------------------------------------------------------------//

#ifndef BASIC_PARTICLE_STATE_HPP
#define BASIC_PARTICLE_STATE_HPP

// Trilinos Includes
#include <Teuchos_ScalarTraits.hpp>

// FACEMC Includes
#include "ParticleType.hpp"
#include "PrintableObject.hpp"
#include "PhaseSpaceDimension.hpp"

namespace FACEMC{

//! The basic particle state struct
class BasicParticleState : public PrintableObject
{

public:

  //! Typedef for energy type
  typedef double energyType;

  //! Typedef for time type
  typedef double timeType;

  //! Typedef for collision number type;
  typedef unsigned collisionNumberType;

private:

  // Typedef for ScalarTraits
  typedef Teuchos::ScalarTraits<double> ST;

public:

  //! Default constructor
  BasicParticleState();

  //! Constructor
  BasicParticleState( const ParticleType type,
		      const double position[3],
		      const double direction[3],
		      const double energy,
		      const double time,
		      const double weight );

  //! Copy constructor
  BasicParticleState( const BasicParticleState& existing_basic_state );

  //! Assignment operator
  BasicParticleState& operator=(
			      const BasicParticleState& existing_basic_state );

  //! Destructor
  virtual ~BasicParticleState()
  { /* ... */ }

  //! Return the particle type
  ParticleType getParticleType() const;

  //! Set the particle type
  ParticleType setParticleType( const ParticleType particle_type );

  //! Return the x position of the particle
  double getXPosition() const;

  //! Return the y position of the particle
  double getYPosition() const;

  //! Return the z position of the particle
  double getZPosition() const;

  //! Return the position of the particle
  const double* getPosition() const;

  //! Set the position of the particle
  void setPosition( const double x_position,
		    const double y_position,
		    const double z_position );

  //! Set the position of the particle
  void setPosition( const double position[3] );

  //! Return the x direction of the particle
  double getXDirection() const;

  //! Return the y direction of the particle
  double getYDirection() const;

  //! Return the z direction of the particle
  double getZDirection() const;

  //! Return the direction of the particle
  const double* getDirection() const;

  //! Set the direction of the particle
  void setDirection( const double x_direction,
		     const double y_direction,
		     const double z_direction );

  //! Set the direction of the particle
  void setDirection( const double direction[3] );

  //! Advance the particle along its direction by the requested distance
  void advance( const double distance );

  //! Return the energy of the particle (MeV)
  double getEnergy() const;

  //! Set the energy of the particle (MeV)
  void setEnergy( const double energy );

  //! Return the time state of the particle (s)
  double getTime() const;

  //! Set the time state of the particle (s)
  void setTime( const double time );

  //! Return the collision number of the particle
  unsigned getCollisionNumber() const;

  //! Increment the collision number of the particle
  void incrementCollisionNumber();

  //! Return the weight of the particle
  double getWeight() const;

  //! Set the weight of the particle
  void setWeight( const double weight );

  //! Multiply the weight of the particle by a factor
  void multiplyWeight( const double weight_factor );

  //! Print method that defines the behavior of the std::stream << operator
  virtual void print( std::ostream& os ) const;

protected:

  //! Print method implementation
  void printImplementation( std::ostream& os ) const;

private:

  // Calculate the velocity of a neutron
  void calculateNeutronVelocity();

  // Particle type
  ParticleType d_type;

  // Position of the particle
  double d_position[3];

  // Direction of the particle
  double d_direction[3];

  // Energy of the particle (MeV)
  double d_energy;

  // Velocity of the particle (cm/s)
  double d_velocity;

  // Time of the particle (s)
  double d_time;

  // Collision number of the particle
  unsigned d_collision_number;

  // Weight o the particle
  double d_weight;
};

} // end FACEMC namespace

#endif // end BASIC_PARTICLE_STATE_HPP

//---------------------------------------------------------------------------//
// end BasicParticleState.hpp
//---------------------------------------------------------------------------//
<commit_msg>Changed the BasicParticleState so that the energy, time, and collision number member data are of type energyType, timeType, and collisionNumberType so that changing the corresponding typedef changes the dimension type.<commit_after>//---------------------------------------------------------------------------//
//!
//! \file   BasicParticleState.hpp
//! \author Alex Robinson
//! \brief  Basic particle state class declaration
//!
//---------------------------------------------------------------------------//

#ifndef BASIC_PARTICLE_STATE_HPP
#define BASIC_PARTICLE_STATE_HPP

// Trilinos Includes
#include <Teuchos_ScalarTraits.hpp>

// FACEMC Includes
#include "ParticleType.hpp"
#include "PrintableObject.hpp"
#include "PhaseSpaceDimension.hpp"

namespace FACEMC{

//! The basic particle state struct
class BasicParticleState : public PrintableObject
{

public:

  //! Typedef for energy type
  typedef double energyType;

  //! Typedef for time type
  typedef double timeType;

  //! Typedef for collision number type;
  typedef unsigned collisionNumberType;

private:

  // Typedef for ScalarTraits
  typedef Teuchos::ScalarTraits<double> ST;

public:

  //! Default constructor
  BasicParticleState();

  //! Constructor
  BasicParticleState( const ParticleType type,
		      const double position[3],
		      const double direction[3],
		      const double energy,
		      const double time,
		      const double weight );

  //! Copy constructor
  BasicParticleState( const BasicParticleState& existing_basic_state );

  //! Assignment operator
  BasicParticleState& operator=(
			      const BasicParticleState& existing_basic_state );

  //! Destructor
  virtual ~BasicParticleState()
  { /* ... */ }

  //! Return the particle type
  ParticleType getParticleType() const;

  //! Set the particle type
  ParticleType setParticleType( const ParticleType particle_type );

  //! Return the x position of the particle
  double getXPosition() const;

  //! Return the y position of the particle
  double getYPosition() const;

  //! Return the z position of the particle
  double getZPosition() const;

  //! Return the position of the particle
  const double* getPosition() const;

  //! Set the position of the particle
  void setPosition( const double x_position,
		    const double y_position,
		    const double z_position );

  //! Set the position of the particle
  void setPosition( const double position[3] );

  //! Return the x direction of the particle
  double getXDirection() const;

  //! Return the y direction of the particle
  double getYDirection() const;

  //! Return the z direction of the particle
  double getZDirection() const;

  //! Return the direction of the particle
  const double* getDirection() const;

  //! Set the direction of the particle
  void setDirection( const double x_direction,
		     const double y_direction,
		     const double z_direction );

  //! Set the direction of the particle
  void setDirection( const double direction[3] );

  //! Advance the particle along its direction by the requested distance
  void advance( const double distance );

  //! Return the energy of the particle (MeV)
  energyType getEnergy() const;

  //! Set the energy of the particle (MeV)
  void setEnergy( const double energy );

  //! Return the time state of the particle (s)
  timeType getTime() const;

  //! Set the time state of the particle (s)
  void setTime( const double time );

  //! Return the collision number of the particle
  collisionNumberType getCollisionNumber() const;

  //! Increment the collision number of the particle
  void incrementCollisionNumber();

  //! Return the weight of the particle
  double getWeight() const;

  //! Set the weight of the particle
  void setWeight( const double weight );

  //! Multiply the weight of the particle by a factor
  void multiplyWeight( const double weight_factor );

  //! Print method that defines the behavior of the std::stream << operator
  virtual void print( std::ostream& os ) const;

protected:

  //! Print method implementation
  void printImplementation( std::ostream& os ) const;

private:

  // Calculate the velocity of a neutron
  void calculateNeutronVelocity();

  // Particle type
  ParticleType d_type;

  // Position of the particle
  double d_position[3];

  // Direction of the particle
  double d_direction[3];

  // Energy of the particle (MeV)
  energyType d_energy;

  // Velocity of the particle (cm/s)
  double d_velocity;

  // Time of the particle (s)
  timeType d_time;

  // Collision number of the particle
  collisionNumberType d_collision_number;

  // Weight o the particle
  double d_weight;
};

} // end FACEMC namespace

#endif // end BASIC_PARTICLE_STATE_HPP

//---------------------------------------------------------------------------//
// end BasicParticleState.hpp
//---------------------------------------------------------------------------//
<|endoftext|>