<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "ParserWorker.h"
#include "Parser.h"
#include "Media.h"
#include "Folder.h"

namespace medialibrary
{
namespace parser
{

Worker::Worker()
    : m_parserCb( nullptr )
    , m_stopParser( false )
    , m_paused( false )
    , m_idle( true )
{
}

void Worker::start()
{
    // Ensure we don't start multiple times.
    assert( m_threads.size() == 0 );
    for ( auto i = 0u; i < m_service->nbThreads(); ++i )
        m_threads.emplace_back( &Worker::mainloop, this );
}

void Worker::pause()
{
    std::lock_guard<compat::Mutex> lock( m_lock );
    m_paused = true;
}

void Worker::resume()
{
    std::lock_guard<compat::Mutex> lock( m_lock );
    m_paused = false;
    m_cond.notify_all();
}

void Worker::signalStop()
{
    for ( auto& t : m_threads )
    {
        if ( t.joinable() )
        {
            std::lock_guard<compat::Mutex> lock( m_lock );
            m_cond.notify_all();
            m_stopParser = true;
        }
    }
}

void Worker::stop()
{
    for ( auto& t : m_threads )
    {
        if ( t.joinable() )
            t.join();
    }
}

void Worker::parse( std::shared_ptr<Task> t )
{
    // Avoid flickering from idle/not idle when not many tasks are running.
    // The thread calling parse for the next parser step might not have
    // something left to do and would turn idle, potentially causing all
    // services to be idle for a very short time, until this parser
    // thread awakes/starts, causing the global parser idle state to be
    // restored back to false.
    // Since we are queuing a task, we already know that this thread is
    // not idle
    setIdle( false );

    if ( m_threads.size() == 0 )
    {
        // Since the thread isn't started, no need to lock the mutex before pushing the task
        m_tasks.push( std::move( t ) );
        start();
    }
    else
    {
        {
            std::lock_guard<compat::Mutex> lock( m_lock );
            m_tasks.push( std::move( t ) );
        }
        m_cond.notify_all();
    }
}

bool Worker::initialize( MediaLibrary* ml, IParserCb* parserCb,
                               std::shared_ptr<IParserService> service )
{
    m_ml = ml;
    m_service = std::move( service );
    m_parserCb = parserCb;
    // Run the service specific initializer
    return m_service->initialize( ml );
}

bool Worker::isIdle() const
{
    return m_idle;
}

void Worker::flush()
{
    std::unique_lock<compat::Mutex> lock( m_lock );
    assert( m_paused == true || m_threads.empty() == true );
    m_idleCond.wait( lock, [this]() {
        return m_idle == true;
    });
    while ( m_tasks.empty() == false )
        m_tasks.pop();
    m_service->onFlushing();
}

void Worker::restart()
{
    m_service->onRestarted();
}

void Worker::mainloop()
{
    // It would be unsafe to call name() at the end of this function, since
    // we might stop the thread during ParserService destruction. This implies
    // that the underlying service has been deleted already.
    std::string serviceName = m_service->name();
    LOG_INFO("Entering ParserService [", serviceName, "] thread");
    setIdle( false );

    while ( m_stopParser == false )
    {
        std::shared_ptr<Task> task;
        {
            std::unique_lock<compat::Mutex> lock( m_lock );
            if ( m_tasks.empty() == true || m_paused == true )
            {
                LOG_DEBUG( "Halting ParserService [", serviceName, "] mainloop" );
                setIdle( true );
                m_idleCond.notify_all();
                m_cond.wait( lock, [this]() {
                    return ( m_tasks.empty() == false && m_paused == false )
                            || m_stopParser == true;
                });
                LOG_DEBUG( "Resuming ParserService [", serviceName, "] mainloop" );
                // We might have been woken up because the parser is being destroyed
                if ( m_stopParser  == true )
                    break;
                setIdle( false );
            }
            // Otherwise it's safe to assume we have at least one element.
            LOG_DEBUG('[', serviceName, "] has ", m_tasks.size(), " tasks remaining" );
            task = std::move( m_tasks.front() );
            m_tasks.pop();
        }
        // Special case to restore uncompleted tasks from a parser thread
        if ( task == nullptr )
        {
            restoreTasks();
            continue;
        }
        if ( task->isStepCompleted( m_service->targetedStep() ) == true )
        {
            LOG_DEBUG( "Skipping completed task [", serviceName, "] on ", task->item().mrl() );
            m_parserCb->done( std::move( task ), Status::Success );
            continue;
        }
        Status status;
        try
        {
            LOG_DEBUG( "Executing ", serviceName, " task on ", task->item().mrl() );
            auto chrono = std::chrono::steady_clock::now();
            auto file = std::static_pointer_cast<File>( task->item().file() );
            auto media = std::static_pointer_cast<Media>( task->item().media() );

            if ( file != nullptr && file->isRemovable() )
            {
                auto folder = Folder::fetch( m_ml, file->folderId() );
                if ( folder->isPresent() == false )
                {
                    LOG_DEBUG( "Postponing parsing of ", file->rawMrl(),
                              " until the device containing it gets mounted back" );
                    m_parserCb->done( std::move( task ), Status::TemporaryUnavailable );
                    continue;
                }
            }
            task->startParserStep();
            status = m_service->run( task->item() );
            auto duration = std::chrono::steady_clock::now() - chrono;
            LOG_DEBUG( "Done executing ", serviceName, " task on ", task->item().mrl(), " in ",
                       std::chrono::duration_cast<std::chrono::milliseconds>( duration ).count(),
                       "ms. Result: ",
                       static_cast<std::underlying_type_t<parser::Status>>( status ) );
        }
        catch ( const fs::DeviceRemovedException& )
        {
            LOG_ERROR( "Parsing of ", task->item().mrl(), " was interrupted "
                       "due to its containing device being unmounted" );
            status = Status::TemporaryUnavailable;
        }
        catch ( const std::exception& ex )
        {
            LOG_ERROR( "Caught an exception during ", task->item().mrl(), " [", serviceName, "] parsing: ", ex.what() );
            status = Status::Fatal;
        }
        if ( handleServiceResult( *task, status ) == false )
            status = Status::Fatal;
        m_parserCb->done( std::move( task ), status );
    }
    LOG_INFO("Exiting ParserService [", serviceName, "] thread");
    setIdle( true );
}

void Worker::setIdle(bool isIdle)
{
    // Calling the idleChanged callback will trigger a call to isIdle, so set the value before
    // invoking it, otherwise we have an incoherent state.
    if ( m_idle != isIdle )
    {
        m_idle = isIdle;
        m_parserCb->onIdleChanged( isIdle );
    }
}

bool Worker::handleServiceResult( Task& task, Status status )
{
    if ( status == Status::Success )
    {
        task.markStepCompleted( m_service->targetedStep() );
        // We don't want to save the extraction step in database, as restarting a
        // task with extraction completed but analysis uncompleted wouldn't run
        // the extraction again, causing the analysis to run with no info.
        if ( m_service->targetedStep() != Step::MetadataExtraction )
            return task.saveParserStep();
        // We don't want to reset the entire retry count, as we would be stuck in
        // a "loop" in case the metadata analysis fails (we'd always reset the retry
        // count to zero, then fail, then run the extraction again, reset the retry,
        // fail the analysis, and so on.
        // We can't not increment the retry count for metadata extraction, since
        // in case a file makes (lib)VLC crash, we would always try again, and
        // therefor we would keep on crashing.
        // However we don't want to just increment the retry count, since it
        // would reach the maximum value too quickly (extraction would set retry
        // count to 1, analysis to 2, and in case of failure, next run would set
        // it over 3, while we only tried 2 times. Instead we just decrement it
        // when the extraction step succeeds
        return task.decrementRetryCount();
    }
    else if ( status == Status::Completed )
    {
        task.markStepCompleted( Step::Completed );
        return task.saveParserStep();
    }
    else if ( status == Status::Discarded )
    {
        return Task::destroy( m_ml, task.id() );
    }
    return true;
}

void Worker::restoreTasks()
{
    auto tasks = Task::fetchUncompleted( m_ml );
    if ( tasks.size() > 0 )
        LOG_INFO( "Resuming parsing on ", tasks.size(), " tasks" );
    else
        LOG_DEBUG( "No task to resume." );
    for ( auto& t : tasks )
    {
        {
            std::lock_guard<compat::Mutex> lock( m_lock );
            if ( m_stopParser == true )
                break;
        }

        if ( t->restoreLinkedEntities() == false )
            continue;
        m_parserCb->parse( std::move( t ) );
    }
}

}
}
<commit_msg>parser: Fix unlikely race condition during start/stop<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "ParserWorker.h"
#include "Parser.h"
#include "Media.h"
#include "Folder.h"

namespace medialibrary
{
namespace parser
{

Worker::Worker()
    : m_parserCb( nullptr )
    , m_stopParser( false )
    , m_paused( false )
    , m_idle( true )
{
}

void Worker::start()
{
    // Ensure we don't start multiple times.
    assert( m_threads.size() == 0 );
    for ( auto i = 0u; i < m_service->nbThreads(); ++i )
        m_threads.emplace_back( &Worker::mainloop, this );
}

void Worker::pause()
{
    std::lock_guard<compat::Mutex> lock( m_lock );
    m_paused = true;
}

void Worker::resume()
{
    std::lock_guard<compat::Mutex> lock( m_lock );
    m_paused = false;
    m_cond.notify_all();
}

void Worker::signalStop()
{
    for ( auto& t : m_threads )
    {
        if ( t.joinable() )
        {
            std::lock_guard<compat::Mutex> lock( m_lock );
            m_cond.notify_all();
            m_stopParser = true;
        }
    }
}

void Worker::stop()
{
    for ( auto& t : m_threads )
    {
        if ( t.joinable() )
            t.join();
    }
}

void Worker::parse( std::shared_ptr<Task> t )
{
    // Avoid flickering from idle/not idle when not many tasks are running.
    // The thread calling parse for the next parser step might not have
    // something left to do and would turn idle, potentially causing all
    // services to be idle for a very short time, until this parser
    // thread awakes/starts, causing the global parser idle state to be
    // restored back to false.
    // Since we are queuing a task, we already know that this thread is
    // not idle
    setIdle( false );

    // Even if no threads appear to be started, we need to lock this in case
    // we're currently doing a stop/start
    {
        std::lock_guard<compat::Mutex> lock( m_lock );
        if ( m_threads.size() == 0 )
        {
            m_tasks.push( std::move( t ) );
            start();
            return;
        }
        m_tasks.push( std::move( t ) );
    }
    m_cond.notify_all();
}

bool Worker::initialize( MediaLibrary* ml, IParserCb* parserCb,
                               std::shared_ptr<IParserService> service )
{
    m_ml = ml;
    m_service = std::move( service );
    m_parserCb = parserCb;
    // Run the service specific initializer
    return m_service->initialize( ml );
}

bool Worker::isIdle() const
{
    return m_idle;
}

void Worker::flush()
{
    std::unique_lock<compat::Mutex> lock( m_lock );
    assert( m_paused == true || m_threads.empty() == true );
    m_idleCond.wait( lock, [this]() {
        return m_idle == true;
    });
    while ( m_tasks.empty() == false )
        m_tasks.pop();
    m_service->onFlushing();
}

void Worker::restart()
{
    m_service->onRestarted();
}

void Worker::mainloop()
{
    // It would be unsafe to call name() at the end of this function, since
    // we might stop the thread during ParserService destruction. This implies
    // that the underlying service has been deleted already.
    std::string serviceName = m_service->name();
    LOG_INFO("Entering ParserService [", serviceName, "] thread");
    setIdle( false );

    while ( m_stopParser == false )
    {
        std::shared_ptr<Task> task;
        {
            std::unique_lock<compat::Mutex> lock( m_lock );
            if ( m_tasks.empty() == true || m_paused == true )
            {
                LOG_DEBUG( "Halting ParserService [", serviceName, "] mainloop" );
                setIdle( true );
                m_idleCond.notify_all();
                m_cond.wait( lock, [this]() {
                    return ( m_tasks.empty() == false && m_paused == false )
                            || m_stopParser == true;
                });
                LOG_DEBUG( "Resuming ParserService [", serviceName, "] mainloop" );
                // We might have been woken up because the parser is being destroyed
                if ( m_stopParser  == true )
                    break;
                setIdle( false );
            }
            // Otherwise it's safe to assume we have at least one element.
            LOG_DEBUG('[', serviceName, "] has ", m_tasks.size(), " tasks remaining" );
            task = std::move( m_tasks.front() );
            m_tasks.pop();
        }
        // Special case to restore uncompleted tasks from a parser thread
        if ( task == nullptr )
        {
            restoreTasks();
            continue;
        }
        if ( task->isStepCompleted( m_service->targetedStep() ) == true )
        {
            LOG_DEBUG( "Skipping completed task [", serviceName, "] on ", task->item().mrl() );
            m_parserCb->done( std::move( task ), Status::Success );
            continue;
        }
        Status status;
        try
        {
            LOG_DEBUG( "Executing ", serviceName, " task on ", task->item().mrl() );
            auto chrono = std::chrono::steady_clock::now();
            auto file = std::static_pointer_cast<File>( task->item().file() );
            auto media = std::static_pointer_cast<Media>( task->item().media() );

            if ( file != nullptr && file->isRemovable() )
            {
                auto folder = Folder::fetch( m_ml, file->folderId() );
                if ( folder->isPresent() == false )
                {
                    LOG_DEBUG( "Postponing parsing of ", file->rawMrl(),
                              " until the device containing it gets mounted back" );
                    m_parserCb->done( std::move( task ), Status::TemporaryUnavailable );
                    continue;
                }
            }
            task->startParserStep();
            status = m_service->run( task->item() );
            auto duration = std::chrono::steady_clock::now() - chrono;
            LOG_DEBUG( "Done executing ", serviceName, " task on ", task->item().mrl(), " in ",
                       std::chrono::duration_cast<std::chrono::milliseconds>( duration ).count(),
                       "ms. Result: ",
                       static_cast<std::underlying_type_t<parser::Status>>( status ) );
        }
        catch ( const fs::DeviceRemovedException& )
        {
            LOG_ERROR( "Parsing of ", task->item().mrl(), " was interrupted "
                       "due to its containing device being unmounted" );
            status = Status::TemporaryUnavailable;
        }
        catch ( const std::exception& ex )
        {
            LOG_ERROR( "Caught an exception during ", task->item().mrl(), " [", serviceName, "] parsing: ", ex.what() );
            status = Status::Fatal;
        }
        if ( handleServiceResult( *task, status ) == false )
            status = Status::Fatal;
        m_parserCb->done( std::move( task ), status );
    }
    LOG_INFO("Exiting ParserService [", serviceName, "] thread");
    setIdle( true );
}

void Worker::setIdle(bool isIdle)
{
    // Calling the idleChanged callback will trigger a call to isIdle, so set the value before
    // invoking it, otherwise we have an incoherent state.
    if ( m_idle != isIdle )
    {
        m_idle = isIdle;
        m_parserCb->onIdleChanged( isIdle );
    }
}

bool Worker::handleServiceResult( Task& task, Status status )
{
    if ( status == Status::Success )
    {
        task.markStepCompleted( m_service->targetedStep() );
        // We don't want to save the extraction step in database, as restarting a
        // task with extraction completed but analysis uncompleted wouldn't run
        // the extraction again, causing the analysis to run with no info.
        if ( m_service->targetedStep() != Step::MetadataExtraction )
            return task.saveParserStep();
        // We don't want to reset the entire retry count, as we would be stuck in
        // a "loop" in case the metadata analysis fails (we'd always reset the retry
        // count to zero, then fail, then run the extraction again, reset the retry,
        // fail the analysis, and so on.
        // We can't not increment the retry count for metadata extraction, since
        // in case a file makes (lib)VLC crash, we would always try again, and
        // therefor we would keep on crashing.
        // However we don't want to just increment the retry count, since it
        // would reach the maximum value too quickly (extraction would set retry
        // count to 1, analysis to 2, and in case of failure, next run would set
        // it over 3, while we only tried 2 times. Instead we just decrement it
        // when the extraction step succeeds
        return task.decrementRetryCount();
    }
    else if ( status == Status::Completed )
    {
        task.markStepCompleted( Step::Completed );
        return task.saveParserStep();
    }
    else if ( status == Status::Discarded )
    {
        return Task::destroy( m_ml, task.id() );
    }
    return true;
}

void Worker::restoreTasks()
{
    auto tasks = Task::fetchUncompleted( m_ml );
    if ( tasks.size() > 0 )
        LOG_INFO( "Resuming parsing on ", tasks.size(), " tasks" );
    else
        LOG_DEBUG( "No task to resume." );
    for ( auto& t : tasks )
    {
        {
            std::lock_guard<compat::Mutex> lock( m_lock );
            if ( m_stopParser == true )
                break;
        }

        if ( t->restoreLinkedEntities() == false )
            continue;
        m_parserCb->parse( std::move( t ) );
    }
}

}
}
<|endoftext|>


<commit_before>#ifndef VEXCL_BACKEND_CUDA_CONTEXT_HPP
#define VEXCL_BACKEND_CUDA_CONTEXT_HPP

/*
The MIT License

Copyright (c) 2012-2018 Denis Demidov <dennis.demidov@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   vexcl/backend/cuda/context.hpp
 * \author Denis Demidov <dennis.demidov@gmail.com>
 * \brief  CUDA device enumeration and context initialization.
 */

#include <vector>
#include <tuple>
#include <iostream>
#include <memory>

#include <cuda.h>

namespace vex {
namespace backend {

/// The CUDA backend.
namespace cuda {

inline CUresult do_init() {
    static const CUresult rc = cuInit(0);
    return rc;
}

namespace detail {

template <class Handle>
struct deleter_impl;

template <>
struct deleter_impl<CUcontext> {
    static void dispose(CUcontext context) {
        cuda_check( cuCtxSetCurrent(context) );
        cuda_check( cuCtxSynchronize()       );
        cuda_check( cuCtxDestroy(context)    );
    }
};

template <>
struct deleter_impl<CUmodule> {
    static void dispose(CUmodule module) {
        cuda_check( cuModuleUnload(module) );
    }
};

template <>
struct deleter_impl<CUstream> {
    static void dispose(CUstream stream) {
        cuda_check( cuStreamDestroy(stream) );
    }
};

// Knows how to dispose of various CUDA handles.
struct deleter {
    deleter(CUcontext ctx) : ctx(ctx) {}

    template <class Handle>
    void operator()(Handle handle) const {
	if (ctx) cuda_check( cuCtxSetCurrent(ctx) );
        deleter_impl<Handle>::dispose(handle);
    }

    CUcontext ctx;
};

}

/// Wrapper around CUdevice.
class device {
    public:
	/// Empty constructor.
	device() {}

        /// Constructor.
        device(CUdevice d) : d(d) {}

        /// Returns raw CUdevice handle.
        CUdevice raw() const { return d; }

        /// Returns raw CUdevice handle.
        operator CUdevice() const { return d; }

        /// Returns name of the device.
        std::string name() const {
            char name[256];
            cuda_check( cuDeviceGetName(name, 256, d) );
            return name;
        }

        /// Returns device compute capability as a tuple of major and minor version numbers.
        std::tuple<int, int> compute_capability() const {
            int major, minor;

            cuda_check( cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, d) );
            cuda_check( cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, d) );

            return std::make_tuple(major, minor);
        }

        /// Returns of multiprocessors on the device.
        size_t multiprocessor_count() const {
            int n;
            cuda_check( cuDeviceGetAttribute(&n, CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, d) );
            return n;
        }

        /// Returns maximum number of threads per block.
        size_t max_threads_per_block() const {
            int n;
            cuda_check( cuDeviceGetAttribute(&n, CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK, d) );
            return n;
        }

        /// Returns maximum amount of shared memory available to a thread block in bytes.
        size_t max_shared_memory_per_block() const {
            int n;
            cuda_check( cuDeviceGetAttribute(&n, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK, d) );
            return n;
        }

        size_t warp_size() const {
            int n;
            cuda_check( cuDeviceGetAttribute(&n, CU_DEVICE_ATTRIBUTE_WARP_SIZE, d) );
            return n;
        }
    private:
        CUdevice d;
};

/// Wrapper around CUcontext.
class context {
    public:
        /// Empty constructor.
        context() {}

        /// Creates a CUDA context.
        context(device dev, unsigned flags = 0)
            : c( create(dev, flags), detail::deleter(0) )
        {
            cuda_check( do_init() );
        }

        /// Returns raw CUcontext handle.
        CUcontext raw() const {
            return c.get();
        }

        /// Returns raw CUcontext handle.
        operator CUcontext() const {
            return c.get();
        }

        /// Binds the context to the calling CPU thread.
        void set_current() const {
            cuda_check( cuCtxSetCurrent( c.get() ) );
        }

    private:
        std::shared_ptr<std::remove_pointer<CUcontext>::type> c;

        static CUcontext create(device dev, unsigned flags) {
            CUcontext h = 0;
            cuda_check( cuCtxCreate(&h, flags, dev.raw()) );
            return h;
        }
};

/// Command queue creation flags.
/**
 * typedef'ed to cl_command_queue_properties in the OpenCL backend and to
 * unsigned in the CUDA backend. See OpenCL/CUDA documentation for the possible
 * values.
 */
typedef unsigned command_queue_properties;

/// Command queue.
/** With the CUDA backend, this is a wrapper around CUstream. */
class command_queue {
    public:
	/// Empty constructor.
	command_queue() {}

        /// Create command queue for the given context and device.
        command_queue(const vex::backend::context &ctx, vex::backend::device dev, unsigned flags)
            : ctx(ctx), dev(dev), s( create(ctx, flags), detail::deleter(ctx.raw()) ), f(flags)
        { }

        /// Blocks until all previously queued commands in command_queue are issued to the associated device and have completed.
        void finish() const {
            ctx.set_current();
            cuda_check( cuStreamSynchronize( s.get() ) );
        }

        /// Returns the context associated with the command queue.
        vex::backend::context context() const {
            return ctx;
        }

        /// Returns the device associated with the command queue.
        vex::backend::device device() const {
            return dev;
        }

        /// Returns command_queue_properties specified at creation.
        unsigned flags() const {
            return f;
        }

        /// Returns raw CUstream handle for the command queue.
        CUstream raw() const {
            return s.get();
        }

        /// Returns raw CUstream handle for the command queue.
        operator CUstream() const {
            return s.get();
        }

    private:
        vex::backend::context  ctx;
        vex::backend::device   dev;
        std::shared_ptr<std::remove_pointer<CUstream>::type> s;
        unsigned f;

        static CUstream create(const vex::backend::context &ctx, unsigned flags = 0) {
            ctx.set_current();

            CUstream s;
            cuda_check( cuStreamCreate(&s, flags) );

            return s;
        }
};

/// CUmodule wrapper.
struct program {
    public:
        program() {}

        program(vex::backend::context c, CUmodule m)
            : c(c), m(m, detail::deleter(c.raw()))
        {}

        CUmodule raw() const {
            return m.get();
        }

        operator CUmodule() const {
            return m.get();
        }
    private:
        vex::backend::context  c;
        std::shared_ptr<std::remove_pointer<CUmodule>::type> m;
};

/// Binds the specified CUDA context to the calling CPU thread.
inline void select_context(const command_queue &q) {
    q.context().set_current();
}

/// Raw device handle.
typedef CUdevice  device_id;

/// Returns device associated with the given queue.
inline device get_device(const command_queue &q) {
    return q.device();
}

/// Returns id of the device associated with the given queue.
inline device_id get_device_id(const command_queue &q) {
    return q.device().raw();
}

/// Launch grid size.
struct ndrange {
    size_t x, y, z;
    ndrange(size_t x = 1, size_t y = 1, size_t z = 1)
        : x(x), y(y), z(z) {}
};

typedef CUcontext context_id;

/// Returns raw context id for the given queue.
inline context_id get_context_id(const command_queue &q) {
    return q.context().raw();
}

/// Returns context for the given queue.
inline context get_context(const command_queue &q) {
    return q.context();
}

/// Compares contexts by raw ids.
struct compare_contexts {
    bool operator()(const context &a, const context &b) const {
        return a.raw() < b.raw();
    }
};

/// Compares queues by raw ids.
struct compare_queues {
    bool operator()(const command_queue &a, const command_queue &b) const {
        return a.raw() < b.raw();
    }
};

/// Create command queue on the same context and device as the given one.
inline command_queue duplicate_queue(const command_queue &q) {
    return command_queue(q.context(), q.device(), q.flags());
}

/// Checks if the compute device is CPU.
/**
 * Always returns false with the CUDA backend.
 */
inline bool is_cpu(const command_queue&) {
    return false;
}

/// Select devices by given criteria.
/**
 * \param filter  Device filter functor. Functors may be combined with logical
 *                operators.
 * \returns list of devices satisfying the provided filter.
 */
template<class DevFilter>
std::vector<device> device_list(DevFilter&& filter) {
    cuda_check( do_init() );

    std::vector<device> device;

    int ndev;
    cuda_check( cuDeviceGetCount(&ndev) );

    for(int d = 0; d < ndev; ++d) {
        try {
            CUdevice dev;
            cuda_check( cuDeviceGet(&dev, d) );
            if (!filter(dev)) continue;
            device.push_back(dev);
        } catch(const error&) { }
    }

    return device;
}

/// Create command queues on devices by given criteria.
/**
 * \param filter  Device filter functor. Functors may be combined with logical
 *                operators.
 * \param properties Command queue properties.
 *
 * \returns list of queues accociated with selected devices.
 * \see device_list
 */
template<class DevFilter>
std::pair< std::vector<context>, std::vector<command_queue> >
queue_list(DevFilter &&filter, unsigned queue_flags = 0)
{
    cuda_check( do_init() );

    std::vector<context>       ctx;
    std::vector<command_queue> queue;

    int ndev;
    cuda_check( cuDeviceGetCount(&ndev) );

    for(int d = 0; d < ndev; ++d) {
        try {
            CUdevice dev;
            cuda_check( cuDeviceGet(&dev, d) );
            if (!filter(dev)) continue;

            context       c(dev);
            command_queue q(c, dev, queue_flags);

            ctx.push_back(c);
            queue.push_back(q);
        } catch(const error&) { }
    }

    return std::make_pair(ctx, queue);
}

} // namespace cuda
} // namespace backend
} // namespace vex

namespace std {

/// Output device name to stream.
inline std::ostream& operator<<(std::ostream &os, const vex::backend::cuda::device &d)
{
    return os << d.name();
}

/// Output device name to stream.
inline std::ostream& operator<<(std::ostream &os, const vex::backend::cuda::command_queue &q)
{
    return os << q.device();
}

} // namespace std

#endif
<commit_msg>:retab!<commit_after>#ifndef VEXCL_BACKEND_CUDA_CONTEXT_HPP
#define VEXCL_BACKEND_CUDA_CONTEXT_HPP

/*
The MIT License

Copyright (c) 2012-2018 Denis Demidov <dennis.demidov@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   vexcl/backend/cuda/context.hpp
 * \author Denis Demidov <dennis.demidov@gmail.com>
 * \brief  CUDA device enumeration and context initialization.
 */

#include <vector>
#include <tuple>
#include <iostream>
#include <memory>

#include <cuda.h>

namespace vex {
namespace backend {

/// The CUDA backend.
namespace cuda {

inline CUresult do_init() {
    static const CUresult rc = cuInit(0);
    return rc;
}

namespace detail {

template <class Handle>
struct deleter_impl;

template <>
struct deleter_impl<CUcontext> {
    static void dispose(CUcontext context) {
        cuda_check( cuCtxSetCurrent(context) );
        cuda_check( cuCtxSynchronize()       );
        cuda_check( cuCtxDestroy(context)    );
    }
};

template <>
struct deleter_impl<CUmodule> {
    static void dispose(CUmodule module) {
        cuda_check( cuModuleUnload(module) );
    }
};

template <>
struct deleter_impl<CUstream> {
    static void dispose(CUstream stream) {
        cuda_check( cuStreamDestroy(stream) );
    }
};

// Knows how to dispose of various CUDA handles.
struct deleter {
    deleter(CUcontext ctx) : ctx(ctx) {}

    template <class Handle>
    void operator()(Handle handle) const {
        if (ctx) cuda_check( cuCtxSetCurrent(ctx) );
        deleter_impl<Handle>::dispose(handle);
    }

    CUcontext ctx;
};

}

/// Wrapper around CUdevice.
class device {
    public:
        /// Empty constructor.
        device() {}

        /// Constructor.
        device(CUdevice d) : d(d) {}

        /// Returns raw CUdevice handle.
        CUdevice raw() const { return d; }

        /// Returns raw CUdevice handle.
        operator CUdevice() const { return d; }

        /// Returns name of the device.
        std::string name() const {
            char name[256];
            cuda_check( cuDeviceGetName(name, 256, d) );
            return name;
        }

        /// Returns device compute capability as a tuple of major and minor version numbers.
        std::tuple<int, int> compute_capability() const {
            int major, minor;

            cuda_check( cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, d) );
            cuda_check( cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, d) );

            return std::make_tuple(major, minor);
        }

        /// Returns of multiprocessors on the device.
        size_t multiprocessor_count() const {
            int n;
            cuda_check( cuDeviceGetAttribute(&n, CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, d) );
            return n;
        }

        /// Returns maximum number of threads per block.
        size_t max_threads_per_block() const {
            int n;
            cuda_check( cuDeviceGetAttribute(&n, CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK, d) );
            return n;
        }

        /// Returns maximum amount of shared memory available to a thread block in bytes.
        size_t max_shared_memory_per_block() const {
            int n;
            cuda_check( cuDeviceGetAttribute(&n, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK, d) );
            return n;
        }

        size_t warp_size() const {
            int n;
            cuda_check( cuDeviceGetAttribute(&n, CU_DEVICE_ATTRIBUTE_WARP_SIZE, d) );
            return n;
        }
    private:
        CUdevice d;
};

/// Wrapper around CUcontext.
class context {
    public:
        /// Empty constructor.
        context() {}

        /// Creates a CUDA context.
        context(device dev, unsigned flags = 0)
            : c( create(dev, flags), detail::deleter(0) )
        {
            cuda_check( do_init() );
        }

        /// Returns raw CUcontext handle.
        CUcontext raw() const {
            return c.get();
        }

        /// Returns raw CUcontext handle.
        operator CUcontext() const {
            return c.get();
        }

        /// Binds the context to the calling CPU thread.
        void set_current() const {
            cuda_check( cuCtxSetCurrent( c.get() ) );
        }

    private:
        std::shared_ptr<std::remove_pointer<CUcontext>::type> c;

        static CUcontext create(device dev, unsigned flags) {
            CUcontext h = 0;
            cuda_check( cuCtxCreate(&h, flags, dev.raw()) );
            return h;
        }
};

/// Command queue creation flags.
/**
 * typedef'ed to cl_command_queue_properties in the OpenCL backend and to
 * unsigned in the CUDA backend. See OpenCL/CUDA documentation for the possible
 * values.
 */
typedef unsigned command_queue_properties;

/// Command queue.
/** With the CUDA backend, this is a wrapper around CUstream. */
class command_queue {
    public:
        /// Empty constructor.
        command_queue() {}

        /// Create command queue for the given context and device.
        command_queue(const vex::backend::context &ctx, vex::backend::device dev, unsigned flags)
            : ctx(ctx), dev(dev), s( create(ctx, flags), detail::deleter(ctx.raw()) ), f(flags)
        { }

        /// Blocks until all previously queued commands in command_queue are issued to the associated device and have completed.
        void finish() const {
            ctx.set_current();
            cuda_check( cuStreamSynchronize( s.get() ) );
        }

        /// Returns the context associated with the command queue.
        vex::backend::context context() const {
            return ctx;
        }

        /// Returns the device associated with the command queue.
        vex::backend::device device() const {
            return dev;
        }

        /// Returns command_queue_properties specified at creation.
        unsigned flags() const {
            return f;
        }

        /// Returns raw CUstream handle for the command queue.
        CUstream raw() const {
            return s.get();
        }

        /// Returns raw CUstream handle for the command queue.
        operator CUstream() const {
            return s.get();
        }

    private:
        vex::backend::context  ctx;
        vex::backend::device   dev;
        std::shared_ptr<std::remove_pointer<CUstream>::type> s;
        unsigned f;

        static CUstream create(const vex::backend::context &ctx, unsigned flags = 0) {
            ctx.set_current();

            CUstream s;
            cuda_check( cuStreamCreate(&s, flags) );

            return s;
        }
};

/// CUmodule wrapper.
struct program {
    public:
        program() {}

        program(vex::backend::context c, CUmodule m)
            : c(c), m(m, detail::deleter(c.raw()))
        {}

        CUmodule raw() const {
            return m.get();
        }

        operator CUmodule() const {
            return m.get();
        }
    private:
        vex::backend::context  c;
        std::shared_ptr<std::remove_pointer<CUmodule>::type> m;
};

/// Binds the specified CUDA context to the calling CPU thread.
inline void select_context(const command_queue &q) {
    q.context().set_current();
}

/// Raw device handle.
typedef CUdevice  device_id;

/// Returns device associated with the given queue.
inline device get_device(const command_queue &q) {
    return q.device();
}

/// Returns id of the device associated with the given queue.
inline device_id get_device_id(const command_queue &q) {
    return q.device().raw();
}

/// Launch grid size.
struct ndrange {
    size_t x, y, z;
    ndrange(size_t x = 1, size_t y = 1, size_t z = 1)
        : x(x), y(y), z(z) {}
};

typedef CUcontext context_id;

/// Returns raw context id for the given queue.
inline context_id get_context_id(const command_queue &q) {
    return q.context().raw();
}

/// Returns context for the given queue.
inline context get_context(const command_queue &q) {
    return q.context();
}

/// Compares contexts by raw ids.
struct compare_contexts {
    bool operator()(const context &a, const context &b) const {
        return a.raw() < b.raw();
    }
};

/// Compares queues by raw ids.
struct compare_queues {
    bool operator()(const command_queue &a, const command_queue &b) const {
        return a.raw() < b.raw();
    }
};

/// Create command queue on the same context and device as the given one.
inline command_queue duplicate_queue(const command_queue &q) {
    return command_queue(q.context(), q.device(), q.flags());
}

/// Checks if the compute device is CPU.
/**
 * Always returns false with the CUDA backend.
 */
inline bool is_cpu(const command_queue&) {
    return false;
}

/// Select devices by given criteria.
/**
 * \param filter  Device filter functor. Functors may be combined with logical
 *                operators.
 * \returns list of devices satisfying the provided filter.
 */
template<class DevFilter>
std::vector<device> device_list(DevFilter&& filter) {
    cuda_check( do_init() );

    std::vector<device> device;

    int ndev;
    cuda_check( cuDeviceGetCount(&ndev) );

    for(int d = 0; d < ndev; ++d) {
        try {
            CUdevice dev;
            cuda_check( cuDeviceGet(&dev, d) );
            if (!filter(dev)) continue;
            device.push_back(dev);
        } catch(const error&) { }
    }

    return device;
}

/// Create command queues on devices by given criteria.
/**
 * \param filter  Device filter functor. Functors may be combined with logical
 *                operators.
 * \param properties Command queue properties.
 *
 * \returns list of queues accociated with selected devices.
 * \see device_list
 */
template<class DevFilter>
std::pair< std::vector<context>, std::vector<command_queue> >
queue_list(DevFilter &&filter, unsigned queue_flags = 0)
{
    cuda_check( do_init() );

    std::vector<context>       ctx;
    std::vector<command_queue> queue;

    int ndev;
    cuda_check( cuDeviceGetCount(&ndev) );

    for(int d = 0; d < ndev; ++d) {
        try {
            CUdevice dev;
            cuda_check( cuDeviceGet(&dev, d) );
            if (!filter(dev)) continue;

            context       c(dev);
            command_queue q(c, dev, queue_flags);

            ctx.push_back(c);
            queue.push_back(q);
        } catch(const error&) { }
    }

    return std::make_pair(ctx, queue);
}

} // namespace cuda
} // namespace backend
} // namespace vex

namespace std {

/// Output device name to stream.
inline std::ostream& operator<<(std::ostream &os, const vex::backend::cuda::device &d)
{
    return os << d.name();
}

/// Output device name to stream.
inline std::ostream& operator<<(std::ostream &os, const vex::backend::cuda::command_queue &q)
{
    return os << q.device();
}

} // namespace std

#endif
<|endoftext|>


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

#include "views/touchui/touch_factory.h"

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/cursorfont.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XIproto.h>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "ui/base/x/x11_util.h"

namespace {

// The X cursor is hidden if it is idle for kCursorIdleSeconds seconds.
int kCursorIdleSeconds = 5;

// Given the TouchParam, return the correspoding valuator index using
// the X device information through Atom name matching.
char FindTPValuator(Display* display,
                    XIDeviceInfo* info,
                    views::TouchFactory::TouchParam touch_param) {
  // Lookup table for mapping TouchParam to Atom string used in X.
  // A full set of Atom strings can be found at xserver-properties.h.
  // For Slot ID, See this chromeos revision: http://git.chromium.org/gitweb/?
  //        p=chromiumos/overlays/chromiumos-overlay.git;
  //        a=commit;h=9164d0a75e48c4867e4ef4ab51f743ae231c059a
  static struct {
    views::TouchFactory::TouchParam tp;
    const char* atom;
  } kTouchParamAtom[] = {
    { views::TouchFactory::TP_TOUCH_MAJOR, "Abs MT Touch Major" },
    { views::TouchFactory::TP_TOUCH_MINOR, "Abs MT Touch Minor" },
    { views::TouchFactory::TP_ORIENTATION, "Abs MT Orientation" },
    { views::TouchFactory::TP_SLOT_ID,     "Abs MT Slot ID" },
    { views::TouchFactory::TP_TRACKING_ID, "Abs MT Tracking ID" },
    { views::TouchFactory::TP_LAST_ENTRY, NULL },
  };

  const char* atom_tp = NULL;

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTouchParamAtom); i++) {
    if (touch_param == kTouchParamAtom[i].tp) {
      atom_tp = kTouchParamAtom[i].atom;
      break;
    }
  }

  if (!atom_tp)
    return -1;

  for (int i = 0; i < info->num_classes; i++) {
    if (info->classes[i]->type != XIValuatorClass)
      continue;
    XIValuatorClassInfo* v =
        reinterpret_cast<XIValuatorClassInfo*>(info->classes[i]);

    const char* atom = XGetAtomName(display, v->label);
    if (atom && strcmp(atom, atom_tp) == 0)
      return v->number;
  }

  return -1;
}

// Setup XInput2 select for the GtkWidget.
gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams,
                                  const GValue* pvalues, gpointer data) {
  GtkWidget* widget = GTK_WIDGET(g_value_get_object(pvalues));
  GdkWindow* window = widget->window;
  views::TouchFactory* factory = static_cast<views::TouchFactory*>(data);

  if (GDK_WINDOW_TYPE(window) != GDK_WINDOW_TOPLEVEL &&
      GDK_WINDOW_TYPE(window) != GDK_WINDOW_CHILD &&
      GDK_WINDOW_TYPE(window) != GDK_WINDOW_DIALOG)
    return true;

  factory->SetupXI2ForXWindow(GDK_WINDOW_XID(window));
  return true;
}

// We need to capture all the GDK windows that get created, and start
// listening for XInput2 events. So we setup a callback to the 'realize'
// signal for GTK+ widgets, so that whenever the signal triggers for any
// GtkWidget, which means the GtkWidget should now have a GdkWindow, we can
// setup XInput2 events for the GdkWindow.
guint realize_signal_id = 0;
guint realize_hook_id = 0;

void SetupGtkWidgetRealizeNotifier(views::TouchFactory* factory) {
  gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET);

  g_signal_parse_name("realize", GTK_TYPE_WIDGET,
                      &realize_signal_id, NULL, FALSE);
  realize_hook_id = g_signal_add_emission_hook(realize_signal_id, 0,
      GtkWidgetRealizeCallback, static_cast<gpointer>(factory), NULL);

  g_type_class_unref(klass);
}

void RemoveGtkWidgetRealizeNotifier() {
  if (realize_signal_id != 0)
    g_signal_remove_emission_hook(realize_signal_id, realize_hook_id);
  realize_signal_id = 0;
  realize_hook_id = 0;
}

}  // namespace

namespace views {

// static
TouchFactory* TouchFactory::GetInstance() {
  return Singleton<TouchFactory>::get();
}

TouchFactory::TouchFactory()
    : is_cursor_visible_(true),
      cursor_timer_(),
      pointer_device_lookup_(),
      touch_device_list_(),
      slots_used_() {
  char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  XColor black;
  black.red = black.green = black.blue = 0;
  Display* display = ui::GetXDisplay();
  Pixmap blank = XCreateBitmapFromData(display, ui::GetX11RootWindow(),
                                       nodata, 8, 8);
  invisible_cursor_ = XCreatePixmapCursor(display, blank, blank,
                                          &black, &black, 0, 0);
  arrow_cursor_ = XCreateFontCursor(display, XC_arrow);

  SetCursorVisible(false, false);
  UpdateDeviceList(display);

  // TODO(sad): Here, we only setup so that the X windows created by GTK+ are
  // setup for XInput2 events. We need a way to listen for XInput2 events for X
  // windows created by other means (e.g. for context menus).
  SetupGtkWidgetRealizeNotifier(this);

  // Make sure the list of devices is kept up-to-date by listening for
  // XI_HierarchyChanged event on the root window.
  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  memset(mask, 0, sizeof(mask));

  XISetMask(mask, XI_HierarchyChanged);

  XIEventMask evmask;
  evmask.deviceid = XIAllDevices;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  XISelectEvents(display, ui::GetX11RootWindow(), &evmask, 1);
}

TouchFactory::~TouchFactory() {
  SetCursorVisible(true, false);
  Display* display = ui::GetXDisplay();
  XFreeCursor(display, invisible_cursor_);
  XFreeCursor(display, arrow_cursor_);

  RemoveGtkWidgetRealizeNotifier();
}

void TouchFactory::UpdateDeviceList(Display* display) {
  // Detect touch devices.
  // NOTE: The new API for retrieving the list of devices (XIQueryDevice) does
  // not provide enough information to detect a touch device. As a result, the
  // old version of query function (XListInputDevices) is used instead.
  // If XInput2 is not supported, this will return null (with count of -1) so
  // we assume there cannot be any touch devices.
  int count = 0;
  touch_device_lookup_.reset();
  touch_device_list_.clear();
  XDeviceInfo* devlist = XListInputDevices(display, &count);
  for (int i = 0; i < count; i++) {
    if (devlist[i].type) {
      const char* devtype = XGetAtomName(display, devlist[i].type);
      if (devtype && !strcmp(devtype, XI_TOUCHSCREEN)) {
        touch_device_lookup_[devlist[i].id] = true;
        touch_device_list_.push_back(devlist[i].id);
      }
    }
  }
  if (devlist)
    XFreeDeviceList(devlist);

  // Instead of asking X for the list of devices all the time, let's maintain a
  // list of pointer devices we care about.
  // It should not be necessary to select for slave devices. XInput2 provides
  // enough information to the event callback to decide which slave device
  // triggered the event, thus decide whether the 'pointer event' is a
  // 'mouse event' or a 'touch event'.
  // However, on some desktops, some events from a master pointer are
  // not delivered to the client. So we select for slave devices instead.
  // If the touch device has 'GrabDevice' set and 'SendCoreEvents' unset (which
  // is possible), then the device is detected as a floating device, and a
  // floating device is not connected to a master device. So it is necessary to
  // also select on the floating devices.
  pointer_device_lookup_.reset();
  XIDeviceInfo* devices = XIQueryDevice(display, XIAllDevices, &count);
  for (int i = 0; i < count; i++) {
    XIDeviceInfo* devinfo = devices + i;
    if (devinfo->use == XIFloatingSlave || devinfo->use == XISlavePointer) {
      pointer_device_lookup_[devinfo->deviceid] = true;
    }
  }
  XIFreeDeviceInfo(devices);

  SetupValuator();
}

bool TouchFactory::ShouldProcessXI2Event(XEvent* xev) {
  DCHECK_EQ(GenericEvent, xev->type);

  XGenericEventCookie* cookie = &xev->xcookie;
  if (cookie->evtype != XI_ButtonPress &&
      cookie->evtype != XI_ButtonRelease &&
      cookie->evtype != XI_Motion)
    return true;

  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(cookie->data);
  return pointer_device_lookup_[xiev->sourceid];
}

void TouchFactory::SetupXI2ForXWindow(Window window) {
  // Setup mask for mouse events. It is possible that a device is loaded/plugged
  // in after we have setup XInput2 on a window. In such cases, we need to
  // either resetup XInput2 for the window, so that we get events from the new
  // device, or we need to listen to events from all devices, and then filter
  // the events from uninteresting devices. We do the latter because that's
  // simpler.

  Display* display = ui::GetXDisplay();

  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  memset(mask, 0, sizeof(mask));

  XISetMask(mask, XI_ButtonPress);
  XISetMask(mask, XI_ButtonRelease);
  XISetMask(mask, XI_Motion);

  XIEventMask evmask;
  evmask.deviceid = XIAllDevices;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  XISelectEvents(display, window, &evmask, 1);
  XFlush(display);
}

void TouchFactory::SetTouchDeviceList(
    const std::vector<unsigned int>& devices) {
  touch_device_lookup_.reset();
  touch_device_list_.clear();
  for (std::vector<unsigned int>::const_iterator iter = devices.begin();
       iter != devices.end(); ++iter) {
    DCHECK(*iter < touch_device_lookup_.size());
    touch_device_lookup_[*iter] = true;
    touch_device_list_.push_back(*iter);
  }

  SetupValuator();
}

bool TouchFactory::IsTouchDevice(unsigned deviceid) const {
  return deviceid < touch_device_lookup_.size() ?
      touch_device_lookup_[deviceid] : false;
}

bool TouchFactory::IsSlotUsed(int slot) const {
  CHECK_LT(slot, kMaxTouchPoints);
  return slots_used_[slot];
}

void TouchFactory::SetSlotUsed(int slot, bool used) {
  CHECK_LT(slot, kMaxTouchPoints);
  slots_used_[slot] = used;
}

bool TouchFactory::GrabTouchDevices(Display* display, ::Window window) {
  if (touch_device_list_.empty())
    return true;

  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  bool success = true;

  memset(mask, 0, sizeof(mask));
  XISetMask(mask, XI_ButtonPress);
  XISetMask(mask, XI_ButtonRelease);
  XISetMask(mask, XI_Motion);

  XIEventMask evmask;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  for (std::vector<int>::const_iterator iter =
       touch_device_list_.begin();
       iter != touch_device_list_.end(); ++iter) {
    evmask.deviceid = *iter;
    Status status = XIGrabDevice(display, *iter, window, CurrentTime, None,
                                 GrabModeAsync, GrabModeAsync, False, &evmask);
    success = success && status == GrabSuccess;
  }

  return success;
}

bool TouchFactory::UngrabTouchDevices(Display* display) {
  bool success = true;
  for (std::vector<int>::const_iterator iter =
       touch_device_list_.begin();
       iter != touch_device_list_.end(); ++iter) {
    Status status = XIUngrabDevice(display, *iter, CurrentTime);
    success = success && status == GrabSuccess;
  }
  return success;
}

void TouchFactory::SetCursorVisible(bool show, bool start_timer) {
  // The cursor is going to be shown. Reset the timer for hiding it.
  if (show && start_timer) {
    cursor_timer_.Stop();
    cursor_timer_.Start(base::TimeDelta::FromSeconds(kCursorIdleSeconds),
        this, &TouchFactory::HideCursorForInactivity);
  } else {
    cursor_timer_.Stop();
  }

  if (show == is_cursor_visible_)
    return;

  is_cursor_visible_ = show;

  Display* display = ui::GetXDisplay();
  Window window = DefaultRootWindow(display);

  if (is_cursor_visible_) {
    XDefineCursor(display, window, arrow_cursor_);
  } else {
    XDefineCursor(display, window, invisible_cursor_);
  }
}

void TouchFactory::SetupValuator() {
  memset(valuator_lookup_, -1, sizeof(valuator_lookup_));

  Display* display = ui::GetXDisplay();
  int ndevice;
  XIDeviceInfo* info_list = XIQueryDevice(display, XIAllDevices, &ndevice);

  for (int i = 0; i < ndevice; i++) {
    XIDeviceInfo* info = info_list + i;

    if (!IsTouchDevice(info->deviceid))
      continue;

    for (int i = 0; i < TP_LAST_ENTRY; i++) {
      TouchParam tp = static_cast<TouchParam>(i);
      valuator_lookup_[info->deviceid][i] = FindTPValuator(display, info, tp);
    }
  }

  if (info_list)
    XIFreeDeviceInfo(info_list);
}

bool TouchFactory::ExtractTouchParam(const XEvent& xev,
                                     TouchParam tp,
                                     float* value) {
  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
  if (xiev->sourceid >= kMaxDeviceNum)
    return false;
  int v = valuator_lookup_[xiev->sourceid][tp];
  if (v >= 0 && XIMaskIsSet(xiev->valuators.mask, v)) {
    *value = xiev->valuators.values[v];
    return true;
  }

  return false;
}

}  // namespace views
<commit_msg>Fix a crash when list of devices is NULL (e.g. over VNC/NX sessions).<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/touchui/touch_factory.h"

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/cursorfont.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XIproto.h>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "ui/base/x/x11_util.h"

namespace {

// The X cursor is hidden if it is idle for kCursorIdleSeconds seconds.
int kCursorIdleSeconds = 5;

// Given the TouchParam, return the correspoding valuator index using
// the X device information through Atom name matching.
char FindTPValuator(Display* display,
                    XIDeviceInfo* info,
                    views::TouchFactory::TouchParam touch_param) {
  // Lookup table for mapping TouchParam to Atom string used in X.
  // A full set of Atom strings can be found at xserver-properties.h.
  // For Slot ID, See this chromeos revision: http://git.chromium.org/gitweb/?
  //        p=chromiumos/overlays/chromiumos-overlay.git;
  //        a=commit;h=9164d0a75e48c4867e4ef4ab51f743ae231c059a
  static struct {
    views::TouchFactory::TouchParam tp;
    const char* atom;
  } kTouchParamAtom[] = {
    { views::TouchFactory::TP_TOUCH_MAJOR, "Abs MT Touch Major" },
    { views::TouchFactory::TP_TOUCH_MINOR, "Abs MT Touch Minor" },
    { views::TouchFactory::TP_ORIENTATION, "Abs MT Orientation" },
    { views::TouchFactory::TP_SLOT_ID,     "Abs MT Slot ID" },
    { views::TouchFactory::TP_TRACKING_ID, "Abs MT Tracking ID" },
    { views::TouchFactory::TP_LAST_ENTRY, NULL },
  };

  const char* atom_tp = NULL;

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTouchParamAtom); i++) {
    if (touch_param == kTouchParamAtom[i].tp) {
      atom_tp = kTouchParamAtom[i].atom;
      break;
    }
  }

  if (!atom_tp)
    return -1;

  for (int i = 0; i < info->num_classes; i++) {
    if (info->classes[i]->type != XIValuatorClass)
      continue;
    XIValuatorClassInfo* v =
        reinterpret_cast<XIValuatorClassInfo*>(info->classes[i]);

    const char* atom = XGetAtomName(display, v->label);
    if (atom && strcmp(atom, atom_tp) == 0)
      return v->number;
  }

  return -1;
}

// Setup XInput2 select for the GtkWidget.
gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams,
                                  const GValue* pvalues, gpointer data) {
  GtkWidget* widget = GTK_WIDGET(g_value_get_object(pvalues));
  GdkWindow* window = widget->window;
  views::TouchFactory* factory = static_cast<views::TouchFactory*>(data);

  if (GDK_WINDOW_TYPE(window) != GDK_WINDOW_TOPLEVEL &&
      GDK_WINDOW_TYPE(window) != GDK_WINDOW_CHILD &&
      GDK_WINDOW_TYPE(window) != GDK_WINDOW_DIALOG)
    return true;

  factory->SetupXI2ForXWindow(GDK_WINDOW_XID(window));
  return true;
}

// We need to capture all the GDK windows that get created, and start
// listening for XInput2 events. So we setup a callback to the 'realize'
// signal for GTK+ widgets, so that whenever the signal triggers for any
// GtkWidget, which means the GtkWidget should now have a GdkWindow, we can
// setup XInput2 events for the GdkWindow.
guint realize_signal_id = 0;
guint realize_hook_id = 0;

void SetupGtkWidgetRealizeNotifier(views::TouchFactory* factory) {
  gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET);

  g_signal_parse_name("realize", GTK_TYPE_WIDGET,
                      &realize_signal_id, NULL, FALSE);
  realize_hook_id = g_signal_add_emission_hook(realize_signal_id, 0,
      GtkWidgetRealizeCallback, static_cast<gpointer>(factory), NULL);

  g_type_class_unref(klass);
}

void RemoveGtkWidgetRealizeNotifier() {
  if (realize_signal_id != 0)
    g_signal_remove_emission_hook(realize_signal_id, realize_hook_id);
  realize_signal_id = 0;
  realize_hook_id = 0;
}

}  // namespace

namespace views {

// static
TouchFactory* TouchFactory::GetInstance() {
  return Singleton<TouchFactory>::get();
}

TouchFactory::TouchFactory()
    : is_cursor_visible_(true),
      cursor_timer_(),
      pointer_device_lookup_(),
      touch_device_list_(),
      slots_used_() {
  char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  XColor black;
  black.red = black.green = black.blue = 0;
  Display* display = ui::GetXDisplay();
  Pixmap blank = XCreateBitmapFromData(display, ui::GetX11RootWindow(),
                                       nodata, 8, 8);
  invisible_cursor_ = XCreatePixmapCursor(display, blank, blank,
                                          &black, &black, 0, 0);
  arrow_cursor_ = XCreateFontCursor(display, XC_arrow);

  SetCursorVisible(false, false);
  UpdateDeviceList(display);

  // TODO(sad): Here, we only setup so that the X windows created by GTK+ are
  // setup for XInput2 events. We need a way to listen for XInput2 events for X
  // windows created by other means (e.g. for context menus).
  SetupGtkWidgetRealizeNotifier(this);

  // Make sure the list of devices is kept up-to-date by listening for
  // XI_HierarchyChanged event on the root window.
  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  memset(mask, 0, sizeof(mask));

  XISetMask(mask, XI_HierarchyChanged);

  XIEventMask evmask;
  evmask.deviceid = XIAllDevices;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  XISelectEvents(display, ui::GetX11RootWindow(), &evmask, 1);
}

TouchFactory::~TouchFactory() {
  SetCursorVisible(true, false);
  Display* display = ui::GetXDisplay();
  XFreeCursor(display, invisible_cursor_);
  XFreeCursor(display, arrow_cursor_);

  RemoveGtkWidgetRealizeNotifier();
}

void TouchFactory::UpdateDeviceList(Display* display) {
  // Detect touch devices.
  // NOTE: The new API for retrieving the list of devices (XIQueryDevice) does
  // not provide enough information to detect a touch device. As a result, the
  // old version of query function (XListInputDevices) is used instead.
  // If XInput2 is not supported, this will return null (with count of -1) so
  // we assume there cannot be any touch devices.
  int count = 0;
  touch_device_lookup_.reset();
  touch_device_list_.clear();
  XDeviceInfo* devlist = XListInputDevices(display, &count);
  for (int i = 0; i < count; i++) {
    if (devlist[i].type) {
      const char* devtype = XGetAtomName(display, devlist[i].type);
      if (devtype && !strcmp(devtype, XI_TOUCHSCREEN)) {
        touch_device_lookup_[devlist[i].id] = true;
        touch_device_list_.push_back(devlist[i].id);
      }
    }
  }
  if (devlist)
    XFreeDeviceList(devlist);

  // Instead of asking X for the list of devices all the time, let's maintain a
  // list of pointer devices we care about.
  // It should not be necessary to select for slave devices. XInput2 provides
  // enough information to the event callback to decide which slave device
  // triggered the event, thus decide whether the 'pointer event' is a
  // 'mouse event' or a 'touch event'.
  // However, on some desktops, some events from a master pointer are
  // not delivered to the client. So we select for slave devices instead.
  // If the touch device has 'GrabDevice' set and 'SendCoreEvents' unset (which
  // is possible), then the device is detected as a floating device, and a
  // floating device is not connected to a master device. So it is necessary to
  // also select on the floating devices.
  pointer_device_lookup_.reset();
  XIDeviceInfo* devices = XIQueryDevice(display, XIAllDevices, &count);
  for (int i = 0; i < count; i++) {
    XIDeviceInfo* devinfo = devices + i;
    if (devinfo->use == XIFloatingSlave || devinfo->use == XISlavePointer) {
      pointer_device_lookup_[devinfo->deviceid] = true;
    }
  }
  if (devices)
    XIFreeDeviceInfo(devices);

  SetupValuator();
}

bool TouchFactory::ShouldProcessXI2Event(XEvent* xev) {
  DCHECK_EQ(GenericEvent, xev->type);

  XGenericEventCookie* cookie = &xev->xcookie;
  if (cookie->evtype != XI_ButtonPress &&
      cookie->evtype != XI_ButtonRelease &&
      cookie->evtype != XI_Motion)
    return true;

  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(cookie->data);
  return pointer_device_lookup_[xiev->sourceid];
}

void TouchFactory::SetupXI2ForXWindow(Window window) {
  // Setup mask for mouse events. It is possible that a device is loaded/plugged
  // in after we have setup XInput2 on a window. In such cases, we need to
  // either resetup XInput2 for the window, so that we get events from the new
  // device, or we need to listen to events from all devices, and then filter
  // the events from uninteresting devices. We do the latter because that's
  // simpler.

  Display* display = ui::GetXDisplay();

  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  memset(mask, 0, sizeof(mask));

  XISetMask(mask, XI_ButtonPress);
  XISetMask(mask, XI_ButtonRelease);
  XISetMask(mask, XI_Motion);

  XIEventMask evmask;
  evmask.deviceid = XIAllDevices;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  XISelectEvents(display, window, &evmask, 1);
  XFlush(display);
}

void TouchFactory::SetTouchDeviceList(
    const std::vector<unsigned int>& devices) {
  touch_device_lookup_.reset();
  touch_device_list_.clear();
  for (std::vector<unsigned int>::const_iterator iter = devices.begin();
       iter != devices.end(); ++iter) {
    DCHECK(*iter < touch_device_lookup_.size());
    touch_device_lookup_[*iter] = true;
    touch_device_list_.push_back(*iter);
  }

  SetupValuator();
}

bool TouchFactory::IsTouchDevice(unsigned deviceid) const {
  return deviceid < touch_device_lookup_.size() ?
      touch_device_lookup_[deviceid] : false;
}

bool TouchFactory::IsSlotUsed(int slot) const {
  CHECK_LT(slot, kMaxTouchPoints);
  return slots_used_[slot];
}

void TouchFactory::SetSlotUsed(int slot, bool used) {
  CHECK_LT(slot, kMaxTouchPoints);
  slots_used_[slot] = used;
}

bool TouchFactory::GrabTouchDevices(Display* display, ::Window window) {
  if (touch_device_list_.empty())
    return true;

  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
  bool success = true;

  memset(mask, 0, sizeof(mask));
  XISetMask(mask, XI_ButtonPress);
  XISetMask(mask, XI_ButtonRelease);
  XISetMask(mask, XI_Motion);

  XIEventMask evmask;
  evmask.mask_len = sizeof(mask);
  evmask.mask = mask;
  for (std::vector<int>::const_iterator iter =
       touch_device_list_.begin();
       iter != touch_device_list_.end(); ++iter) {
    evmask.deviceid = *iter;
    Status status = XIGrabDevice(display, *iter, window, CurrentTime, None,
                                 GrabModeAsync, GrabModeAsync, False, &evmask);
    success = success && status == GrabSuccess;
  }

  return success;
}

bool TouchFactory::UngrabTouchDevices(Display* display) {
  bool success = true;
  for (std::vector<int>::const_iterator iter =
       touch_device_list_.begin();
       iter != touch_device_list_.end(); ++iter) {
    Status status = XIUngrabDevice(display, *iter, CurrentTime);
    success = success && status == GrabSuccess;
  }
  return success;
}

void TouchFactory::SetCursorVisible(bool show, bool start_timer) {
  // The cursor is going to be shown. Reset the timer for hiding it.
  if (show && start_timer) {
    cursor_timer_.Stop();
    cursor_timer_.Start(base::TimeDelta::FromSeconds(kCursorIdleSeconds),
        this, &TouchFactory::HideCursorForInactivity);
  } else {
    cursor_timer_.Stop();
  }

  if (show == is_cursor_visible_)
    return;

  is_cursor_visible_ = show;

  Display* display = ui::GetXDisplay();
  Window window = DefaultRootWindow(display);

  if (is_cursor_visible_) {
    XDefineCursor(display, window, arrow_cursor_);
  } else {
    XDefineCursor(display, window, invisible_cursor_);
  }
}

void TouchFactory::SetupValuator() {
  memset(valuator_lookup_, -1, sizeof(valuator_lookup_));

  Display* display = ui::GetXDisplay();
  int ndevice;
  XIDeviceInfo* info_list = XIQueryDevice(display, XIAllDevices, &ndevice);

  for (int i = 0; i < ndevice; i++) {
    XIDeviceInfo* info = info_list + i;

    if (!IsTouchDevice(info->deviceid))
      continue;

    for (int i = 0; i < TP_LAST_ENTRY; i++) {
      TouchParam tp = static_cast<TouchParam>(i);
      valuator_lookup_[info->deviceid][i] = FindTPValuator(display, info, tp);
    }
  }

  if (info_list)
    XIFreeDeviceInfo(info_list);
}

bool TouchFactory::ExtractTouchParam(const XEvent& xev,
                                     TouchParam tp,
                                     float* value) {
  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
  if (xiev->sourceid >= kMaxDeviceNum)
    return false;
  int v = valuator_lookup_[xiev->sourceid][tp];
  if (v >= 0 && XIMaskIsSet(xiev->valuators.mask, v)) {
    *value = xiev->valuators.values[v];
    return true;
  }

  return false;
}

}  // namespace views
<|endoftext|>


<commit_before>/***********************************************************************
    created:    7/2/2008
    author:     Paul D Turner
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2009 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUISamplesConfig.h"
#include "CEGuiBaseApplication.h"
#include "SamplesFramework.h"
#include "CEGUI/System.h"
#include "CEGUI/DefaultResourceProvider.h"
#include "CEGUI/ImageManager.h"
#include "CEGUI/Image.h"
#include "CEGUI/Font.h"
#include "CEGUI/Scheme.h"
#include "CEGUI/WindowManager.h"
#include "CEGUI/falagard/WidgetLookManager.h"
#include "CEGUI/ScriptModule.h"
#include "CEGUI/XMLParser.h"
#include "CEGUI/GeometryBuffer.h"
#include "CEGUI/GUIContext.h"
#include "CEGUI/RenderTarget.h"
#include "CEGUI/AnimationManager.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>

#ifdef __APPLE__
#   include <Carbon/Carbon.h>
#endif

// setup default-default path
#ifndef CEGUI_SAMPLE_DATAPATH
    #define CEGUI_SAMPLE_DATAPATH "../datafiles"
#endif

/***********************************************************************
    Static / Const data
*************************************************************************/
const char CEGuiBaseApplication::DATAPATH_VAR_NAME[] = "CEGUI_SAMPLE_DATAPATH";
SamplesFrameworkBase* CEGuiBaseApplication::d_sampleApp(0);

//----------------------------------------------------------------------------//
CEGuiBaseApplication::CEGuiBaseApplication() :
    d_quitting(false),
    d_renderer(0),
    d_imageCodec(0),
    d_resourceProvider(0),
    d_logoGeometry(0),
    d_FPSGeometry(0),
    d_FPSElapsed(0.0f),
    d_FPSFrames(0),
    d_FPSValue(0),
    d_spinLogo(false)
{
}

//----------------------------------------------------------------------------//
CEGuiBaseApplication::~CEGuiBaseApplication()
{
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::renderSingleFrame(const float elapsed)
{
    CEGUI::System& gui_system(CEGUI::System::getSingleton());

    gui_system.injectTimePulse(elapsed);
    d_sampleApp->update(static_cast<float>(elapsed));

    updateFPS(elapsed);
    updateLogo(elapsed);

    beginRendering(elapsed);

    CEGUI::Renderer* gui_renderer(gui_system.getRenderer());
    gui_renderer->beginRendering();

    d_sampleApp->renderGUIContexts();

    gui_renderer->endRendering();
    CEGUI::WindowManager::getSingleton().cleanDeadPool();

    endRendering();
}

//----------------------------------------------------------------------------//
bool CEGuiBaseApplication::execute(SamplesFrameworkBase* sampleApp)
{
    d_sampleApp = sampleApp;

    if (!d_renderer)
        throw CEGUI::InvalidRequestException("CEGuiBaseApplication::run: "
            "Base application subclass did not create Renderer!");

    // start up CEGUI system using objects created in subclass constructor.
    CEGUI::System::create(*d_renderer, d_resourceProvider, 0, d_imageCodec);

    // initialise resource system
    initialiseResourceGroupDirectories();
    initialiseDefaultResourceGroups();

    const CEGUI::Rectf scrn(CEGUI::Vector2f(0, 0), d_renderer->getDisplaySize());

    // setup for FPS value
    d_FPSGeometry = &d_renderer->createGeometryBuffer();
    d_FPSGeometry->setClippingRegion(scrn);
    positionFPS();

    // setup for spinning logo
    d_logoGeometry = &d_renderer->createGeometryBuffer();
    d_logoGeometry->setPivot(CEGUI::Vector3f(91.5f, 44.5f, 0));
    positionLogo();

    // create logo imageset and draw the image (we only ever draw this once)
    CEGUI::ImageManager::getSingleton().addFromImageFile("cegui_logo",
                                                         "logo.png");
    CEGUI::ImageManager::getSingleton().get("cegui_logo").render(
        *d_logoGeometry, CEGUI::Rectf(0, 0, 183, 89), 0, CEGUI::ColourRect(0xFFFFFFFF));

    // clearing this queue actually makes sure it's created(!)
    CEGUI::System::getSingleton().getDefaultGUIContext().clearGeometry(CEGUI::RQ_OVERLAY);

    // subscribe handler to render overlay items
    CEGUI::System::getSingleton().getDefaultGUIContext().
        subscribeEvent(CEGUI::RenderingSurface::EventRenderQueueStarted,
            CEGUI::Event::Subscriber(&CEGuiBaseApplication::sampleBrowserOverlayHandler,
                                     this));

    // subscribe handler to reposition logo when window is sized.
    CEGUI::System::getSingleton().subscribeEvent(
        CEGUI::System::EventDisplaySizeChanged,
        CEGUI::Event::Subscriber(&CEGuiBaseApplication::resizeHandler,
        this));

    const CEGUI::Rectf& area(CEGUI::System::getSingleton().getRenderer()->
                             getDefaultRenderTarget().getArea());
    d_sampleApp->setApplicationWindowSize(static_cast<int>(area.getWidth()),
                                          static_cast<int>(area.getHeight()));

    run();

    cleanup();
    destroyWindow();

    return true;
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::cleanup()
{
    CEGUI::ImageManager::getSingleton().destroy("cegui_logo");
    d_renderer->destroyGeometryBuffer(*d_logoGeometry);
    d_renderer->destroyGeometryBuffer(*d_FPSGeometry);
    CEGUI::System::destroy();
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::initialiseResourceGroupDirectories()
{
    // initialise the required dirs for the DefaultResourceProvider
    CEGUI::DefaultResourceProvider* rp =
        static_cast<CEGUI::DefaultResourceProvider*>
            (CEGUI::System::getSingleton().getResourceProvider());

    const char* dataPathPrefix = getDataPathPrefix();
    char resourcePath[PATH_MAX];

    // for each resource type, set a resource group directory
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "schemes/");
    rp->setResourceGroupDirectory("schemes", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "imagesets/");
    rp->setResourceGroupDirectory("imagesets", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "fonts/");
    rp->setResourceGroupDirectory("fonts", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "layouts/");
    rp->setResourceGroupDirectory("layouts", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "looknfeel/");
    rp->setResourceGroupDirectory("looknfeels", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "lua_scripts/");
    rp->setResourceGroupDirectory("lua_scripts", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "xml_schemas/");
    rp->setResourceGroupDirectory("schemas", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "animations/");
    rp->setResourceGroupDirectory("animations", resourcePath);
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::initialiseDefaultResourceGroups()
{
    // set the default resource groups to be used
    CEGUI::ImageManager::setImagesetDefaultResourceGroup("imagesets");
    CEGUI::Font::setDefaultResourceGroup("fonts");
    CEGUI::Scheme::setDefaultResourceGroup("schemes");
    CEGUI::WidgetLookManager::setDefaultResourceGroup("looknfeels");
    CEGUI::WindowManager::setDefaultResourceGroup("layouts");
    CEGUI::ScriptModule::setDefaultResourceGroup("lua_scripts");
    CEGUI::AnimationManager::setDefaultResourceGroup("animations");

    // setup default group for validation schemas
    CEGUI::XMLParser* parser = CEGUI::System::getSingleton().getXMLParser();
    if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))
        parser->setProperty("SchemaDefaultResourceGroup", "schemas");
}

//----------------------------------------------------------------------------//
const char* CEGuiBaseApplication::getDataPathPrefix() const
{
    static char dataPathPrefix[PATH_MAX];

#ifdef __APPLE__
    CFURLRef datafilesURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
                                                    CFSTR("datafiles"),
                                                    0, 0);
    CFURLGetFileSystemRepresentation(datafilesURL, true,
                                     reinterpret_cast<UInt8*>(dataPathPrefix),
                                     PATH_MAX);
    CFRelease(datafilesURL);
#else
    char* envDataPath = 0;

    // get data path from environment var
    envDataPath = getenv(DATAPATH_VAR_NAME);

    // set data path prefix / base directory.  This will
    // be either from an environment variable, or from
    // a compiled in default based on original configure
    // options
    if (envDataPath != 0)
        strcpy(dataPathPrefix, envDataPath);
    else
        strcpy(dataPathPrefix, CEGUI_SAMPLE_DATAPATH);
#endif

    return dataPathPrefix;
}

//----------------------------------------------------------------------------//
bool CEGuiBaseApplication::sampleBrowserOverlayHandler(const CEGUI::EventArgs& args)
{
    if (static_cast<const CEGUI::RenderQueueEventArgs&>(args).queueID != CEGUI::RQ_OVERLAY)
        return false;

    // draw the logo
    d_logoGeometry->draw();
    // draw FPS value
    d_FPSGeometry->draw();

    return true;
}

//----------------------------------------------------------------------------//
bool CEGuiBaseApplication::sampleOverlayHandler(const CEGUI::EventArgs& args)
{
    if (static_cast<const CEGUI::RenderQueueEventArgs&>(args).queueID != CEGUI::RQ_OVERLAY)
        return false;

    // draw FPS value
    d_FPSGeometry->draw();

    return true;
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::updateFPS(const float elapsed)
{
    // another frame
    ++d_FPSFrames;

    if ((d_FPSElapsed += elapsed) >= 1.0f)
    {
        if (d_FPSFrames != d_FPSValue)
        {
            d_FPSValue = d_FPSFrames;

            CEGUI::Font* fnt = CEGUI::System::getSingleton().getDefaultGUIContext().getDefaultFont();
            if (!fnt)
                return;

            // update FPS imagery
            char fps_textbuff[16];
            sprintf(fps_textbuff , "FPS: %d", d_FPSValue);

            d_FPSGeometry->reset();
            fnt->drawText(*d_FPSGeometry, fps_textbuff, CEGUI::Vector2f(0, 0), 0,
                        CEGUI::Colour(0xFFFFFFFF));
        }

        // reset counter state
        d_FPSFrames = 0;

        float modValue = 1.f;
        d_FPSElapsed = std::modf(d_FPSElapsed, &modValue);
    }
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::updateLogo(const float elapsed)
{
    if (!d_spinLogo)
        return;

    static float rot = 0.0f;
    d_logoGeometry->setRotation(CEGUI::Quaternion::eulerAnglesDegrees(rot, 0, 0));

    rot = fmodf(rot + 180.0f * elapsed, 360.0f);
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::positionLogo()
{
    const CEGUI::Rectf scrn(d_renderer->getDefaultRenderTarget().getArea());

    d_logoGeometry->setClippingRegion(scrn);
    d_logoGeometry->setTranslation(
        CEGUI::Vector3f(10.0f, scrn.getSize().d_height - 89.0f, 0.0f));
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::positionFPS()
{
    const CEGUI::Rectf scrn(d_renderer->getDefaultRenderTarget().getArea());

    d_FPSGeometry->setClippingRegion(scrn);
    d_FPSGeometry->setTranslation(
        CEGUI::Vector3f(scrn.getSize().d_width - 120.0f, 0.0f, 0.0f));
}

//----------------------------------------------------------------------------//
bool CEGuiBaseApplication::resizeHandler(const CEGUI::EventArgs& /*args*/)
{
    // clear FPS geometry and see that it gets recreated in the next frame
    d_FPSGeometry->reset();
    d_FPSValue = 0;

    const CEGUI::Rectf& area(CEGUI::System::getSingleton().getRenderer()->
                             getDefaultRenderTarget().getArea());
    d_sampleApp->handleNewWindowSize(area.getWidth(), area.getHeight());

    positionLogo();
    return true;
}

//----------------------------------------------------------------------------//

void CEGuiBaseApplication::registerSampleOverlayHandler(CEGUI::GUIContext* gui_context)
{
    // clearing this queue actually makes sure it's created(!)
    gui_context->clearGeometry(CEGUI::RQ_OVERLAY);

    // subscribe handler to render overlay items
    gui_context->subscribeEvent(CEGUI::RenderingSurface::EventRenderQueueStarted,
        CEGUI::Event::Subscriber(&CEGuiBaseApplication::sampleOverlayHandler,
        this));
}

//----------------------------------------------------------------------------//<commit_msg>MOD: Minor change in CEGuiBaseApplication to retrieve Font directly<commit_after>/***********************************************************************
    created:    7/2/2008
    author:     Paul D Turner
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2009 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUISamplesConfig.h"
#include "CEGuiBaseApplication.h"
#include "SamplesFramework.h"
#include "CEGUI/System.h"
#include "CEGUI/DefaultResourceProvider.h"
#include "CEGUI/ImageManager.h"
#include "CEGUI/Image.h"
#include "CEGUI/Font.h"
#include "CEGUI/Scheme.h"
#include "CEGUI/WindowManager.h"
#include "CEGUI/falagard/WidgetLookManager.h"
#include "CEGUI/ScriptModule.h"
#include "CEGUI/XMLParser.h"
#include "CEGUI/GeometryBuffer.h"
#include "CEGUI/GUIContext.h"
#include "CEGUI/RenderTarget.h"
#include "CEGUI/AnimationManager.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>

#ifdef __APPLE__
#   include <Carbon/Carbon.h>
#endif

// setup default-default path
#ifndef CEGUI_SAMPLE_DATAPATH
    #define CEGUI_SAMPLE_DATAPATH "../datafiles"
#endif

/***********************************************************************
    Static / Const data
*************************************************************************/
const char CEGuiBaseApplication::DATAPATH_VAR_NAME[] = "CEGUI_SAMPLE_DATAPATH";
SamplesFrameworkBase* CEGuiBaseApplication::d_sampleApp(0);

//----------------------------------------------------------------------------//
CEGuiBaseApplication::CEGuiBaseApplication() :
    d_quitting(false),
    d_renderer(0),
    d_imageCodec(0),
    d_resourceProvider(0),
    d_logoGeometry(0),
    d_FPSGeometry(0),
    d_FPSElapsed(0.0f),
    d_FPSFrames(0),
    d_FPSValue(0),
    d_spinLogo(false)
{
}

//----------------------------------------------------------------------------//
CEGuiBaseApplication::~CEGuiBaseApplication()
{
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::renderSingleFrame(const float elapsed)
{
    CEGUI::System& gui_system(CEGUI::System::getSingleton());

    gui_system.injectTimePulse(elapsed);
    d_sampleApp->update(static_cast<float>(elapsed));

    updateFPS(elapsed);
    updateLogo(elapsed);

    beginRendering(elapsed);

    CEGUI::Renderer* gui_renderer(gui_system.getRenderer());
    gui_renderer->beginRendering();

    d_sampleApp->renderGUIContexts();

    gui_renderer->endRendering();
    CEGUI::WindowManager::getSingleton().cleanDeadPool();

    endRendering();
}

//----------------------------------------------------------------------------//
bool CEGuiBaseApplication::execute(SamplesFrameworkBase* sampleApp)
{
    d_sampleApp = sampleApp;

    if (!d_renderer)
        throw CEGUI::InvalidRequestException("CEGuiBaseApplication::run: "
            "Base application subclass did not create Renderer!");

    // start up CEGUI system using objects created in subclass constructor.
    CEGUI::System::create(*d_renderer, d_resourceProvider, 0, d_imageCodec);

    // initialise resource system
    initialiseResourceGroupDirectories();
    initialiseDefaultResourceGroups();

    const CEGUI::Rectf scrn(CEGUI::Vector2f(0, 0), d_renderer->getDisplaySize());

    // setup for FPS value
    d_FPSGeometry = &d_renderer->createGeometryBuffer();
    d_FPSGeometry->setClippingRegion(scrn);
    positionFPS();

    // setup for spinning logo
    d_logoGeometry = &d_renderer->createGeometryBuffer();
    d_logoGeometry->setPivot(CEGUI::Vector3f(91.5f, 44.5f, 0));
    positionLogo();

    // create logo imageset and draw the image (we only ever draw this once)
    CEGUI::ImageManager::getSingleton().addFromImageFile("cegui_logo",
                                                         "logo.png");
    CEGUI::ImageManager::getSingleton().get("cegui_logo").render(
        *d_logoGeometry, CEGUI::Rectf(0, 0, 183, 89), 0, CEGUI::ColourRect(0xFFFFFFFF));

    // clearing this queue actually makes sure it's created(!)
    CEGUI::System::getSingleton().getDefaultGUIContext().clearGeometry(CEGUI::RQ_OVERLAY);

    // subscribe handler to render overlay items
    CEGUI::System::getSingleton().getDefaultGUIContext().
        subscribeEvent(CEGUI::RenderingSurface::EventRenderQueueStarted,
            CEGUI::Event::Subscriber(&CEGuiBaseApplication::sampleBrowserOverlayHandler,
                                     this));

    // subscribe handler to reposition logo when window is sized.
    CEGUI::System::getSingleton().subscribeEvent(
        CEGUI::System::EventDisplaySizeChanged,
        CEGUI::Event::Subscriber(&CEGuiBaseApplication::resizeHandler,
        this));

    const CEGUI::Rectf& area(CEGUI::System::getSingleton().getRenderer()->
                             getDefaultRenderTarget().getArea());
    d_sampleApp->setApplicationWindowSize(static_cast<int>(area.getWidth()),
                                          static_cast<int>(area.getHeight()));

    run();

    cleanup();
    destroyWindow();

    return true;
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::cleanup()
{
    CEGUI::ImageManager::getSingleton().destroy("cegui_logo");
    d_renderer->destroyGeometryBuffer(*d_logoGeometry);
    d_renderer->destroyGeometryBuffer(*d_FPSGeometry);
    CEGUI::System::destroy();
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::initialiseResourceGroupDirectories()
{
    // initialise the required dirs for the DefaultResourceProvider
    CEGUI::DefaultResourceProvider* rp =
        static_cast<CEGUI::DefaultResourceProvider*>
            (CEGUI::System::getSingleton().getResourceProvider());

    const char* dataPathPrefix = getDataPathPrefix();
    char resourcePath[PATH_MAX];

    // for each resource type, set a resource group directory
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "schemes/");
    rp->setResourceGroupDirectory("schemes", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "imagesets/");
    rp->setResourceGroupDirectory("imagesets", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "fonts/");
    rp->setResourceGroupDirectory("fonts", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "layouts/");
    rp->setResourceGroupDirectory("layouts", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "looknfeel/");
    rp->setResourceGroupDirectory("looknfeels", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "lua_scripts/");
    rp->setResourceGroupDirectory("lua_scripts", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "xml_schemas/");
    rp->setResourceGroupDirectory("schemas", resourcePath);
    sprintf(resourcePath, "%s/%s", dataPathPrefix, "animations/");
    rp->setResourceGroupDirectory("animations", resourcePath);
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::initialiseDefaultResourceGroups()
{
    // set the default resource groups to be used
    CEGUI::ImageManager::setImagesetDefaultResourceGroup("imagesets");
    CEGUI::Font::setDefaultResourceGroup("fonts");
    CEGUI::Scheme::setDefaultResourceGroup("schemes");
    CEGUI::WidgetLookManager::setDefaultResourceGroup("looknfeels");
    CEGUI::WindowManager::setDefaultResourceGroup("layouts");
    CEGUI::ScriptModule::setDefaultResourceGroup("lua_scripts");
    CEGUI::AnimationManager::setDefaultResourceGroup("animations");

    // setup default group for validation schemas
    CEGUI::XMLParser* parser = CEGUI::System::getSingleton().getXMLParser();
    if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))
        parser->setProperty("SchemaDefaultResourceGroup", "schemas");
}

//----------------------------------------------------------------------------//
const char* CEGuiBaseApplication::getDataPathPrefix() const
{
    static char dataPathPrefix[PATH_MAX];

#ifdef __APPLE__
    CFURLRef datafilesURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
                                                    CFSTR("datafiles"),
                                                    0, 0);
    CFURLGetFileSystemRepresentation(datafilesURL, true,
                                     reinterpret_cast<UInt8*>(dataPathPrefix),
                                     PATH_MAX);
    CFRelease(datafilesURL);
#else
    char* envDataPath = 0;

    // get data path from environment var
    envDataPath = getenv(DATAPATH_VAR_NAME);

    // set data path prefix / base directory.  This will
    // be either from an environment variable, or from
    // a compiled in default based on original configure
    // options
    if (envDataPath != 0)
        strcpy(dataPathPrefix, envDataPath);
    else
        strcpy(dataPathPrefix, CEGUI_SAMPLE_DATAPATH);
#endif

    return dataPathPrefix;
}

//----------------------------------------------------------------------------//
bool CEGuiBaseApplication::sampleBrowserOverlayHandler(const CEGUI::EventArgs& args)
{
    if (static_cast<const CEGUI::RenderQueueEventArgs&>(args).queueID != CEGUI::RQ_OVERLAY)
        return false;

    // draw the logo
    d_logoGeometry->draw();
    // draw FPS value
    d_FPSGeometry->draw();

    return true;
}

//----------------------------------------------------------------------------//
bool CEGuiBaseApplication::sampleOverlayHandler(const CEGUI::EventArgs& args)
{
    if (static_cast<const CEGUI::RenderQueueEventArgs&>(args).queueID != CEGUI::RQ_OVERLAY)
        return false;

    // draw FPS value
    d_FPSGeometry->draw();

    return true;
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::updateFPS(const float elapsed)
{
    // another frame
    ++d_FPSFrames;

    if ((d_FPSElapsed += elapsed) >= 1.0f)
    {
        if (d_FPSFrames != d_FPSValue)
        {
            d_FPSValue = d_FPSFrames;

            CEGUI::Font* fnt = &CEGUI::FontManager::getSingleton().get("DejaVuSans-12");
            if (!fnt)
                return;

            // update FPS imagery
            char fps_textbuff[16];
            sprintf(fps_textbuff , "FPS: %d", d_FPSValue);

            d_FPSGeometry->reset();
            fnt->drawText(*d_FPSGeometry, fps_textbuff, CEGUI::Vector2f(0, 0), 0,
                        CEGUI::Colour(0xFFFFFFFF));
        }

        // reset counter state
        d_FPSFrames = 0;

        float modValue = 1.f;
        d_FPSElapsed = std::modf(d_FPSElapsed, &modValue);
    }
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::updateLogo(const float elapsed)
{
    if (!d_spinLogo)
        return;

    static float rot = 0.0f;
    d_logoGeometry->setRotation(CEGUI::Quaternion::eulerAnglesDegrees(rot, 0, 0));

    rot = fmodf(rot + 180.0f * elapsed, 360.0f);
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::positionLogo()
{
    const CEGUI::Rectf scrn(d_renderer->getDefaultRenderTarget().getArea());

    d_logoGeometry->setClippingRegion(scrn);
    d_logoGeometry->setTranslation(
        CEGUI::Vector3f(10.0f, scrn.getSize().d_height - 89.0f, 0.0f));
}

//----------------------------------------------------------------------------//
void CEGuiBaseApplication::positionFPS()
{
    const CEGUI::Rectf scrn(d_renderer->getDefaultRenderTarget().getArea());

    d_FPSGeometry->setClippingRegion(scrn);
    d_FPSGeometry->setTranslation(
        CEGUI::Vector3f(scrn.getSize().d_width - 120.0f, 0.0f, 0.0f));
}

//----------------------------------------------------------------------------//
bool CEGuiBaseApplication::resizeHandler(const CEGUI::EventArgs& /*args*/)
{
    // clear FPS geometry and see that it gets recreated in the next frame
    d_FPSGeometry->reset();
    d_FPSValue = 0;

    const CEGUI::Rectf& area(CEGUI::System::getSingleton().getRenderer()->
                             getDefaultRenderTarget().getArea());
    d_sampleApp->handleNewWindowSize(area.getWidth(), area.getHeight());

    positionLogo();
    return true;
}

//----------------------------------------------------------------------------//

void CEGuiBaseApplication::registerSampleOverlayHandler(CEGUI::GUIContext* gui_context)
{
    // clearing this queue actually makes sure it's created(!)
    gui_context->clearGeometry(CEGUI::RQ_OVERLAY);

    // subscribe handler to render overlay items
    gui_context->subscribeEvent(CEGUI::RenderingSurface::EventRenderQueueStarted,
        CEGUI::Event::Subscriber(&CEGuiBaseApplication::sampleOverlayHandler,
        this));
}

//----------------------------------------------------------------------------//<|endoftext|>