<commit_before>#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder/Camera.h"
#include "cinder/MotionManager.h"
#include "cinder/Utilities.h"
#include "cinder/Log.h"

#include <map>

using namespace ci;
using namespace ci::app;
using namespace std;

static const float kTargetSize = 2.0f;
static const float kTargetDistance = 4.0f;
static const float kHorizontalSize = kTargetSize * 4.0f;

class CompassApp : public App {
  public:
	void setup();
	void update();
	void resize();
	void draw();

  private:
	// draw the tex area corresponding to \a d at the specified \a location
	void drawCardinalTex( char d, const vec3 &location, const vec3 &localRotation );

	void drawFps();

	CameraPersp			mCam;
	gl::TextureRef		mCardinalTex;
	map<char, ivec2>	mCardinalPositions;
	ivec2				mCardinalSize;
};

void CompassApp::setup()
{
	getSignalSupportedOrientations().connect( [] { return InterfaceOrientation::All; } );

	if( ! MotionManager::isNorthReliable() )
		CI_LOG_W( "North is not dependable on your device." );

    MotionManager::enable( 60.0f );

	mCam.setEyePoint( vec3( 0 ) );

	// directions.png contains an image of our 6 directions; this is a map of pixel offsets into that texture
	mCardinalSize = ivec2( 300, 300 );
	mCardinalPositions['U'] = vec2( 0, mCardinalSize.y * 2 );
	mCardinalPositions['D'] = vec2( mCardinalSize.x, mCardinalSize.y * 2 );
	mCardinalPositions['S'] = vec2( 0, mCardinalSize.y );
	mCardinalPositions['W'] = vec2( mCardinalSize.x, mCardinalSize.y );
	mCardinalPositions['N'] = vec2( 0, 0 );
	mCardinalPositions['E'] = vec2( mCardinalSize.x, 0 );

	mCardinalTex = gl::Texture::create( loadImage( loadAsset( "directions.png" ) ) );
}

void CompassApp::resize()
{
	bool isPortrait = getWindowHeight() > getWindowWidth();
	float adjustedViewWidth = ( isPortrait ? kHorizontalSize : (kHorizontalSize / kTargetSize) * getWindowAspectRatio() );
	float theta = 2.0f * math<float>::atan2( adjustedViewWidth / 2.0f, kTargetDistance );

	mCam.setPerspective( toDegrees( theta ) , getWindowAspectRatio(), 1, 1000 );
}

void CompassApp::update()
{
	mCam.setOrientation( MotionManager::getRotation( getOrientation() ) );
}

void CompassApp::draw()
{
	gl::clear();
	gl::color( Color::white() );

    gl::pushMatrices();
    gl::setMatrices( mCam );

	static const float kD = kTargetDistance;
	drawCardinalTex( 'N', vec3(  0,  0, -kD ),	vec3(  0,	  M_PI,	  0 ) );
	drawCardinalTex( 'S', vec3(  0,  0,  kD ),	vec3(  0,	   0,	  0 ) );
	drawCardinalTex( 'E', vec3(  kD, 0,  0 ),	vec3(  0,	  M_PI/2, 0 ) );
	drawCardinalTex( 'W', vec3( -kD, 0,  0 ),	vec3(  0,	 -M_PI/2, 0 ) );
	drawCardinalTex( 'U', vec3(  0,  kD, 0 ),	vec3( -M_PI/2, 0,     0 ) );
	drawCardinalTex( 'D', vec3(  0, -kD, 0 ),	vec3(  M_PI/2, 0,     0 ) );

	gl::popMatrices();

	drawFps();
}

void CompassApp::drawCardinalTex( char d, const vec3 &location, const vec3 &localRotation )
{
	const float kRectHalfWidth = kTargetSize / 2.0f;
	Rectf destRect( kRectHalfWidth, kRectHalfWidth, -kRectHalfWidth, -kRectHalfWidth );
	ivec2 texPos = mCardinalPositions[d];
	Area srcArea( texPos.x, texPos.y, texPos.x + mCardinalSize.x, texPos.y + mCardinalSize.y);

	gl::pushModelView();

	gl::translate( location );

#if 1
	gl::rotate( angleAxis( localRotation.x, vec3(1,0,0) ) );
	gl::rotate( angleAxis( localRotation.y, vec3(0,1,0) ) );
	gl::rotate( angleAxis( localRotation.z, vec3(0,0,1) ) );
#else
	// draw billboarded, flip to face eye point
	gl::rotate( mCam.getOrientation() );
	gl::rotate( vec3( 0, M_PI, 0 ) );
#endif
	gl::draw( mCardinalTex, srcArea, destRect );

	gl::popModelView();
}

void CompassApp::drawFps()
{
	gl::enableAlphaBlending();
	gl::drawStringRight( toString( floor(getAverageFps()) ) + " fps", vec2( getWindowWidth(), 40.0f), Color( 0, 0, 1 ), Font( "Helvetica", 30 ) );
	gl::disableAlphaBlending();
}

CINDER_APP( CompassApp, RendererGl )
<commit_msg>Added a wireframe sphere in the background<commit_after>#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder/Camera.h"
#include "cinder/MotionManager.h"
#include "cinder/Utilities.h"
#include "cinder/Log.h"

#include <map>

using namespace ci;
using namespace ci::app;
using namespace std;

static const float kTargetSize = 2.0f;
static const float kTargetDistance = 4.0f;
static const float kHorizontalSize = kTargetSize * 4.0f;

class CompassApp : public App {
  public:
	void setup();
	void update();
	void resize();
	void draw();

  private:
	// draw the tex area corresponding to \a d at the specified \a location
	void drawCardinalTex( char d, const vec3 &location, const vec3 &localRotation );

	void drawFps();

	CameraPersp			mCam;
	gl::TextureRef		mCardinalTex;
	map<char, ivec2>	mCardinalPositions;
	ivec2				mCardinalSize;

	gl::BatchRef		mSphereBatch;
};

void CompassApp::setup()
{
	getSignalSupportedOrientations().connect( [] { return InterfaceOrientation::All; } );

	if( ! MotionManager::isNorthReliable() )
		CI_LOG_W( "North is not dependable on your device." );

    MotionManager::enable( 60.0f );

	mCam.setEyePoint( vec3( 0 ) );

	// directions.png contains an image of our 6 directions; this is a map of pixel offsets into that texture
	mCardinalSize = ivec2( 300, 300 );
	mCardinalPositions['U'] = vec2( 0, mCardinalSize.y * 2 );
	mCardinalPositions['D'] = vec2( mCardinalSize.x, mCardinalSize.y * 2 );
	mCardinalPositions['S'] = vec2( 0, mCardinalSize.y );
	mCardinalPositions['W'] = vec2( mCardinalSize.x, mCardinalSize.y );
	mCardinalPositions['N'] = vec2( 0, 0 );
	mCardinalPositions['E'] = vec2( mCardinalSize.x, 0 );

	mCardinalTex = gl::Texture::create( loadImage( loadAsset( "directions.png" ) ) );

	// add something to look at in the background
	auto geom = geom::Sphere().radius( kTargetSize * 4 ).colors() >> geom::Lines();
	auto glsl = gl::getStockShader( gl::ShaderDef().color() );
	mSphereBatch = gl::Batch::create( geom, glsl );
}

void CompassApp::resize()
{
	bool isPortrait = getWindowHeight() > getWindowWidth();
	float adjustedViewWidth = ( isPortrait ? kHorizontalSize : (kHorizontalSize / kTargetSize) * getWindowAspectRatio() );
	float theta = 2.0f * math<float>::atan2( adjustedViewWidth / 2.0f, kTargetDistance );

	mCam.setPerspective( toDegrees( theta ) , getWindowAspectRatio(), 1, 1000 );
}

void CompassApp::update()
{
	mCam.setOrientation( MotionManager::getRotation( getOrientation() ) );
}

void CompassApp::draw()
{
	gl::clear();
	gl::color( Color::white() );

    gl::pushMatrices();
    gl::setMatrices( mCam );

	mSphereBatch->draw();

	static const float kD = kTargetDistance;
	drawCardinalTex( 'N', vec3(  0,  0, -kD ),	vec3(  0,	  M_PI,	  0 ) );
	drawCardinalTex( 'S', vec3(  0,  0,  kD ),	vec3(  0,	   0,	  0 ) );
	drawCardinalTex( 'E', vec3(  kD, 0,  0 ),	vec3(  0,	  M_PI/2, 0 ) );
	drawCardinalTex( 'W', vec3( -kD, 0,  0 ),	vec3(  0,	 -M_PI/2, 0 ) );
	drawCardinalTex( 'U', vec3(  0,  kD, 0 ),	vec3( -M_PI/2, 0,     0 ) );
	drawCardinalTex( 'D', vec3(  0, -kD, 0 ),	vec3(  M_PI/2, 0,     0 ) );

	gl::popMatrices();

	drawFps();
}

void CompassApp::drawCardinalTex( char d, const vec3 &location, const vec3 &localRotation )
{
	const float kRectHalfWidth = kTargetSize / 2.0f;
	Rectf destRect( kRectHalfWidth, kRectHalfWidth, -kRectHalfWidth, -kRectHalfWidth );
	ivec2 texPos = mCardinalPositions[d];
	Area srcArea( texPos.x, texPos.y, texPos.x + mCardinalSize.x, texPos.y + mCardinalSize.y);

	gl::pushModelView();

	gl::translate( location );

#if 1
	gl::rotate( angleAxis( localRotation.x, vec3(1,0,0) ) );
	gl::rotate( angleAxis( localRotation.y, vec3(0,1,0) ) );
	gl::rotate( angleAxis( localRotation.z, vec3(0,0,1) ) );
#else
	// draw billboarded, flip to face eye point
	gl::rotate( mCam.getOrientation() );
	gl::rotate( M_PI, vec3( 0, 1, 0 ) );
#endif

	gl::draw( mCardinalTex, srcArea, destRect );

	gl::popModelView();
}

void CompassApp::drawFps()
{
	gl::enableAlphaBlending();
	gl::drawStringRight( toString( floor(getAverageFps()) ) + " fps", vec2( getWindowWidth(), 40.0f), Color( 0, 0, 1 ), Font( "Helvetica", 30 ) );
	gl::disableAlphaBlending();
}

CINDER_APP( CompassApp, RendererGl )
<|endoftext|>


<commit_before>#include "cinder/app/AppNative.h"
#include "cinder/gl/gl.h"

#include "sansumbrella/Sandbox.h"
#include "cinder/Rand.h"

using namespace ci;
using namespace ci::app;
using namespace std;

// something like this is a reasonable approach to managing a body in your
// own structure
struct Bubble
{
  Bubble( su::unique_b2Body_ptr &&b, const ci::Vec2f &l, float r ):
  body( move(b) ),
  loc( l ),
  radius( r )
  { // bouncy
    body->GetFixtureList()->SetRestitution( 0.6f );
  }
  // we can't copy-construct because of unique_ptr, but we can move-construct
  // necessary to have this ctor for std::vector support
  Bubble( Bubble &&other ):
  body( move( other.body ) ),
  loc( other.loc ),
  radius( other.radius )
  {}
  su::unique_b2Body_ptr body;
  ci::Vec2f             loc;
  float                 radius;
  // passing in scale on update lets you change it dynamically
  // also, objects don't need to know about where scale is stored/calculated
  void update( float scale )
  {
    loc.x = body->GetPosition().x * scale;
    loc.y = body->GetPosition().y * scale;
  }
  void draw()
  {
    gl::color( Color::white() );
    gl::pushModelView();
    gl::translate( loc );
    gl::drawSolidCircle( Vec2f::zero(), radius );
    gl::rotate( body->GetAngle() * 180 / M_PI );
    gl::color( Color::black() );
    gl::drawLine( Vec2f{ -radius, 0 }, Vec2f{ radius, 0 } );
    gl::popModelView();
  }
};

class DrawingApp : public AppNative {
public:
  void prepareSettings( Settings *settings );
  void setup();
  void update();
  void draw();
  void resetBubbles();

private:
  su::Sandbox   	mSandbox;
  vector<Bubble>  mBubbles;
};

void DrawingApp::prepareSettings(Settings *settings)
{
  settings->setWindowSize( 1024, 768 );
  settings->enableMultiTouch();
}

void DrawingApp::setup()
{
  auto bounds = mSandbox.toPhysics( Rectf{getWindowBounds()} );
  mSandbox.createBoundaryRect( bounds );

  resetBubbles();

  getWindow()->getSignalTouchesEnded().connect( [this]( TouchEvent &event ) -> void
                                               {
                                                 resetBubbles();
                                               }
                                               );
}

void DrawingApp::resetBubbles()
{
  mBubbles.clear();
  auto bounds = mSandbox.toPhysics( Rectf{getWindowBounds()} );
  for( int i = 0; i < 29; ++i )
  {
    Vec2f loc{ Rand::randFloat( bounds.getX1(), bounds.getX2() ), Rand::randFloat( bounds.getY1(), bounds.getY2() ) };
    float radius = Rand::randFloat( 10.0f, 100.0f );
    mBubbles.emplace_back( Bubble{ mSandbox.createCircle( loc, mSandbox.toPhysics(radius) ), mSandbox.fromPhysics(loc), radius } );
  }
}

void DrawingApp::update()
{
  mSandbox.step();
  for( auto &bubble : mBubbles )
  {
    bubble.update( mSandbox.getPointsPerMeter() );
  }
}

void DrawingApp::draw()
{
  // clear out the window with black
  gl::clear( Color( 0, 0, 0 ) );
  for( auto &bubble : mBubbles )
  {
    bubble.draw();
  }
}

CINDER_APP_NATIVE( DrawingApp, RendererGl )
<commit_msg>Minor cleanup of bubble.<commit_after>#include "cinder/app/AppNative.h"
#include "cinder/gl/gl.h"

#include "sansumbrella/Sandbox.h"
#include "cinder/Rand.h"

using namespace ci;
using namespace ci::app;
using namespace std;

// something like this is a reasonable approach to managing a body in your
// own structure
class Bubble
{
public:
  Bubble( su::unique_b2Body_ptr &&b, const ci::Vec2f &l, float r ):
  body( move(b) ),
  loc( l ),
  radius( r )
  { // bouncy
    body->GetFixtureList()->SetRestitution( 0.6f );
  }
  // we can't copy-construct because of unique_ptr, but we can move-construct
  // necessary to have this ctor for std::vector support
  Bubble( Bubble &&other ):
  body( move( other.body ) ),
  loc( other.loc ),
  radius( other.radius )
  {}
  // update the bubble position from physics, using the world scale
  void update( float scale )
  {
    loc.x = body->GetPosition().x * scale;
    loc.y = body->GetPosition().y * scale;
  }
  // draw a circle with a line at bubble location
  void draw()
  {
    gl::color( Color::white() );
    gl::pushModelView();
    gl::translate( loc );
    gl::drawSolidCircle( Vec2f::zero(), radius );
    gl::rotate( body->GetAngle() * 180 / M_PI );
    gl::color( Color::black() );
    gl::drawLine( Vec2f{ -radius, 0 }, Vec2f{ radius, 0 } );
    gl::popModelView();
  }
private:
  su::unique_b2Body_ptr body;
  ci::Vec2f             loc;
  float                 radius;
};

class DrawingApp : public AppNative {
public:
  void prepareSettings( Settings *settings );
  void setup();
  void update();
  void draw();
  void resetBubbles();

private:
  su::Sandbox   	mSandbox;
  vector<Bubble>  mBubbles;
};

void DrawingApp::prepareSettings(Settings *settings)
{
  settings->setWindowSize( 1024, 768 );
  settings->enableMultiTouch();
}

void DrawingApp::setup()
{
  auto bounds = mSandbox.toPhysics( Rectf{getWindowBounds()} );
  mSandbox.createBoundaryRect( bounds );

  resetBubbles();

  getWindow()->getSignalTouchesEnded().connect( [this]( TouchEvent &event ) -> void
                                               {
                                                 resetBubbles();
                                               }
                                               );
}

void DrawingApp::resetBubbles()
{
  mBubbles.clear();
  auto bounds = mSandbox.toPhysics( Rectf{getWindowBounds()} );
  for( int i = 0; i < 29; ++i )
  {
    Vec2f loc{ Rand::randFloat( bounds.getX1(), bounds.getX2() ), Rand::randFloat( bounds.getY1(), bounds.getY2() ) };
    float radius = Rand::randFloat( 10.0f, 100.0f );
    mBubbles.emplace_back( Bubble{ mSandbox.createCircle( loc, mSandbox.toPhysics(radius) ), mSandbox.fromPhysics(loc), radius } );
  }
}

void DrawingApp::update()
{
  mSandbox.step();
  for( auto &bubble : mBubbles )
  {
    bubble.update( mSandbox.getPointsPerMeter() );
  }
}

void DrawingApp::draw()
{
  // clear out the window with black
  gl::clear( Color( 0, 0, 0 ) );
  for( auto &bubble : mBubbles )
  {
    bubble.draw();
  }
}

CINDER_APP_NATIVE( DrawingApp, RendererGl )
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit https://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2016-2018 Francois Beaune, The appleseedhq Organization
//
// 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.
//

// Interface header.
#include "logtarget.h"

// appleseed-max headers.
#include "utilities.h"

// appleseed.foundation headers.
#include "foundation/platform/windows.h"    // include before 3ds Max headers
#include "foundation/utility/string.h"

// Boost headers.
#include "boost/thread/locks.hpp"
#include "boost/thread/mutex.hpp"

// 3ds Max headers.
#include <log.h>
#include <max.h>

// Standard headers.
#include <string>
#include <vector>

namespace asf = foundation;

namespace
{
    typedef std::vector<std::string> StringVec;

    void emit_message(const DWORD type, const StringVec& lines)
    {
        for (const auto& line : lines)
        {
            GetCOREInterface()->Log()->LogEntry(
                type,
                NO_DIALOG,
                L"appleseed",
                L"[appleseed] %s",
                utf8_to_wide(line).c_str());
        }
    }

    struct Message
    {
        DWORD               m_type;
        StringVec           m_lines;
    };

    boost::mutex            g_message_queue_mutex;
    std::vector<Message>    g_message_queue;

    void push_message(const DWORD type, const StringVec& lines)
    {
        boost::mutex::scoped_lock lock(g_message_queue_mutex);

        Message message;
        message.m_type = type;
        message.m_lines = lines;
        g_message_queue.push_back(message);
    }

    void emit_pending_messages()
    {
        boost::mutex::scoped_lock lock(g_message_queue_mutex);

        for (const auto& message : g_message_queue)
            emit_message(message.m_type, message.m_lines);

        g_message_queue.clear();
    }

    const UINT WM_TRIGGER_CALLBACK = WM_USER + 4764;

    bool is_main_thread()
    {
        return GetCOREInterface15()->GetMainThreadID() == GetCurrentThreadId();
    }
}

void LogTarget::release()
{
    delete this;
}

void LogTarget::write(
    const asf::LogMessage::Category category,
    const char*                     file,
    const size_t                    line,
    const char*                     header,
    const char*                     message)
{
    DWORD type;
    switch (category)
    {
      case asf::LogMessage::Debug: type = SYSLOG_DEBUG; break;
      case asf::LogMessage::Info: type = SYSLOG_INFO; break;
      case asf::LogMessage::Warning: type = SYSLOG_WARN; break;
      case asf::LogMessage::Error:
      case asf::LogMessage::Fatal:
      default:
        type = SYSLOG_ERROR;
        break;
    }

    std::vector<std::string> lines;
    asf::split(message, "\n", lines);

    if (is_main_thread())
        emit_message(type, lines);
    else
    {
        push_message(type, lines);
        PostMessage(
            GetCOREInterface()->GetMAXHWnd(),
            WM_TRIGGER_CALLBACK,
            reinterpret_cast<WPARAM>(emit_pending_messages),
            0);
    }
}
<commit_msg>Ensure correct ordering of log messages<commit_after>
//
// This source file is part of appleseed.
// Visit https://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2016-2018 Francois Beaune, The appleseedhq Organization
//
// 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.
//

// Interface header.
#include "logtarget.h"

// appleseed-max headers.
#include "utilities.h"

// appleseed.foundation headers.
#include "foundation/platform/windows.h"    // include before 3ds Max headers
#include "foundation/utility/string.h"

// Boost headers.
#include "boost/thread/locks.hpp"
#include "boost/thread/mutex.hpp"

// 3ds Max headers.
#include <log.h>
#include <max.h>

// Standard headers.
#include <string>
#include <vector>

namespace asf = foundation;

namespace
{
    const UINT WM_TRIGGER_CALLBACK = WM_USER + 4764;

    bool is_main_thread()
    {
        return GetCOREInterface15()->GetMainThreadID() == GetCurrentThreadId();
    }

    typedef std::vector<std::string> StringVec;

    void do_emit_message(const DWORD type, const StringVec& lines)
    {
        assert(is_main_thread());

        for (const auto& line : lines)
        {
            GetCOREInterface()->Log()->LogEntry(
                type,
                NO_DIALOG,
                L"appleseed",
                L"[appleseed] %s",
                utf8_to_wide(line).c_str());
        }
    }

    struct Message
    {
        DWORD               m_type;
        StringVec           m_lines;
    };

    boost::mutex            g_message_queue_mutex;
    std::vector<Message>    g_message_queue;

    void push_message(const DWORD type, const StringVec& lines)
    {
        boost::mutex::scoped_lock lock(g_message_queue_mutex);

        Message message;
        message.m_type = type;
        message.m_lines = lines;
        g_message_queue.push_back(message);
    }

    void emit_pending_messages_no_lock()
    {
        assert(is_main_thread());

        for (const auto& message : g_message_queue)
            do_emit_message(message.m_type, message.m_lines);

        g_message_queue.clear();
    }

    void emit_pending_messages()
    {
        boost::mutex::scoped_lock lock(g_message_queue_mutex);

        emit_pending_messages_no_lock();
    }

    void emit_message(const DWORD type, const StringVec& lines)
    {
        boost::mutex::scoped_lock lock(g_message_queue_mutex);

        emit_pending_messages_no_lock();
        do_emit_message(type, lines);
   }
}

void LogTarget::release()
{
    delete this;
}

void LogTarget::write(
    const asf::LogMessage::Category category,
    const char*                     file,
    const size_t                    line,
    const char*                     header,
    const char*                     message)
{
    DWORD type;
    switch (category)
    {
      case asf::LogMessage::Debug: type = SYSLOG_DEBUG; break;
      case asf::LogMessage::Info: type = SYSLOG_INFO; break;
      case asf::LogMessage::Warning: type = SYSLOG_WARN; break;
      case asf::LogMessage::Error:
      case asf::LogMessage::Fatal:
      default:
        type = SYSLOG_ERROR;
        break;
    }

    std::vector<std::string> lines;
    asf::split(message, "\n", lines);

    if (is_main_thread())
        emit_message(type, lines);
    else
    {
        push_message(type, lines);
        PostMessage(
            GetCOREInterface()->GetMAXHWnd(),
            WM_TRIGGER_CALLBACK,
            reinterpret_cast<WPARAM>(emit_pending_messages),
            0);
    }
}
<|endoftext|>


<commit_before>#include "base.hpp"
#include "ButtonStatus.hpp"
#include "CommonData.hpp"
#include "Config.hpp"
#include "Core.hpp"
#include "RemapClass.hpp"
#include "remap.hpp"
#include "util/CallBackWrapper.hpp"
#include "util/EventInputQueue.hpp"
#include "util/EventOutputQueue.hpp"
#include "util/EventWatcher.hpp"
#include "util/GlobalLock.hpp"
#include "util/KeyboardRepeat.hpp"
#include "util/ListHookedConsumer.hpp"
#include "util/ListHookedKeyboard.hpp"
#include "util/ListHookedPointing.hpp"
#include "util/NumHeldDownKeys.hpp"
#include "util/PressDownKeys.hpp"
#include "util/TimerWrapper.hpp"
#include "RemapFunc/HoldingKeyToKey.hpp"
#include "RemapFunc/KeyOverlaidModifier.hpp"
#include "RemapFunc/PointingRelativeToScroll.hpp"
#include "VirtualKey.hpp"

#include <sys/errno.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOTimerEventSource.h>

namespace org_pqrs_KeyRemap4MacBook {
  namespace Core {
    namespace {
      IOWorkLoop* workLoop = NULL;
    }

    void
    start(void)
    {
      GlobalLock::initialize();
      CommonData::initialize();
      EventWatcher::initialize();
      PressDownKeys::initialize();
      FlagStatus::initialize();
      ButtonStatus::initialize();

      ListHookedKeyboard::instance().initialize();
      ListHookedConsumer::instance().initialize();
      ListHookedPointing::instance().initialize();

      workLoop = IOWorkLoop::workLoop();
      if (! workLoop) {
        IOLOG_ERROR("IOWorkLoop::workLoop failed\n");
      } else {
        KeyboardRepeat::initialize(*workLoop);
        EventInputQueue::initialize(*workLoop);
        VirtualKey::initialize(*workLoop);
        EventOutputQueue::initialize(*workLoop);
        RemapFunc::HoldingKeyToKey::static_initialize(*workLoop);
        RemapFunc::KeyOverlaidModifier::static_initialize(*workLoop);
        RemapFunc::PointingRelativeToScroll::static_initialize(*workLoop);
        ListHookedKeyboard::static_initialize(*workLoop);
        RemapClassManager::initialize(*workLoop);
      }

      Config::sysctl_register();
    }

    void
    stop(void)
    {
      // At first, restoreEventAction for all devices.
      {
        GlobalLock::ScopedLock lk;

        ListHookedKeyboard::instance().terminate();
        ListHookedConsumer::instance().terminate();
        ListHookedPointing::instance().terminate();
      }

      // roughly sleep: waiting for finishing queued device events.
      IOSleep(200);

      // call terminate
      {
        GlobalLock::ScopedLock lk;

        Config::sysctl_unregister();

        RemapClassManager::terminate();
        KeyboardRepeat::terminate();
        EventInputQueue::terminate();
        VirtualKey::terminate();
        EventOutputQueue::terminate();
        RemapFunc::HoldingKeyToKey::static_terminate();
        RemapFunc::KeyOverlaidModifier::static_terminate();
        RemapFunc::PointingRelativeToScroll::static_terminate();
        ListHookedKeyboard::static_terminate();

        if (workLoop) {
          workLoop->release();
          workLoop = NULL;
        }

        EventWatcher::terminate();
        PressDownKeys::terminate();
      }

      CommonData::terminate();
      GlobalLock::terminate();
    }

    // ======================================================================
    bool
    IOHIKeyboard_gIOMatchedNotification_callback(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      GlobalLock::ScopedLock lk;
      if (! lk) return false;

      IOLOG_DEBUG("%s newService:%p\n", __FUNCTION__, newService);

      IOHIDevice* device = OSDynamicCast(IOHIKeyboard, newService);
      if (! device) return false;

      ListHookedKeyboard::instance().push_back(new ListHookedKeyboard::Item(device));
      ListHookedConsumer::instance().push_back(new ListHookedConsumer::Item(device));
      return true;
    }

    bool
    IOHIKeyboard_gIOTerminatedNotification_callback(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      GlobalLock::ScopedLock lk;
      if (! lk) return false;

      IOLOG_DEBUG("%s newService:%p\n", __FUNCTION__, newService);

      IOHIDevice* device = OSDynamicCast(IOHIKeyboard, newService);
      if (! device) return false;

      ListHookedKeyboard::instance().erase(device);
      ListHookedConsumer::instance().erase(device);
      return true;
    }

    bool
    IOHIPointing_gIOMatchedNotification_callback(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      GlobalLock::ScopedLock lk;
      if (! lk) return false;

      IOLOG_DEBUG("%s newService:%p\n", __FUNCTION__, newService);

      IOHIDevice* device = OSDynamicCast(IOHIPointing, newService);
      if (! device) return false;

      ListHookedPointing::instance().push_back(new ListHookedPointing::Item(device));
      return true;
    }

    bool
    IOHIPointing_gIOTerminatedNotification_callback(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      GlobalLock::ScopedLock lk;
      if (! lk) return false;

      IOLOG_DEBUG("%s newService:%p\n", __FUNCTION__, newService);

      IOHIDevice* device = OSDynamicCast(IOHIPointing, newService);
      if (! device) return false;

      ListHookedPointing::instance().erase(device);
      return true;
    }

    // ======================================================================
    void
    remap_KeyboardEventCallback(Params_KeyboardEventCallBack& params)
    {
      params.log();

      // ------------------------------------------------------------
      RemapParams remapParams(params);

      // ------------------------------------------------------------
      FlagStatus::set(params.key, params.flags);

      RemapClassManager::remap_key(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(params.eventType, FlagStatus::makeFlags(), params.key,
                                                                                params.charCode, params.charSet, params.origCharCode, params.origCharSet,
                                                                                params.keyboardType, false));
        if (ptr) {
          KeyboardRepeat::set(*ptr);
          EventOutputQueue::FireKey::fire(*ptr);
        }
      }

      if (NumHeldDownKeys::iszero()) {
        NumHeldDownKeys::reset();
        KeyboardRepeat::cancel();
        EventWatcher::reset();
        FlagStatus::reset();
        ButtonStatus::reset();
        VirtualKey::reset();
        EventOutputQueue::FireModifiers::fire(FlagStatus::makeFlags());
        EventOutputQueue::FireRelativePointer::fire();
        PressDownKeys::clear();
      }

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }

    void
    remap_KeyboardSpecialEventCallback(Params_KeyboardSpecialEventCallback& params)
    {
      params.log();

      RemapConsumerParams remapParams(params);

      // ------------------------------------------------------------
      RemapClassManager::remap_consumer(remapParams);

      // ----------------------------------------
      if (! remapParams.isremapped) {
        Params_KeyboardSpecialEventCallback::auto_ptr ptr(Params_KeyboardSpecialEventCallback::alloc(params.eventType, FlagStatus::makeFlags(), params.key,
                                                                                params.flavor, params.guid, false));
        if (ptr) {
          KeyboardRepeat::set(*ptr);
          EventOutputQueue::FireConsumer::fire(*ptr);
        }
      }

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }

    void
    remap_RelativePointerEventCallback(Params_RelativePointerEventCallback& params)
    {
      params.log();

      RemapPointingParams_relative remapParams(params);

      ButtonStatus::set(params.ex_button, params.ex_isbuttondown);

      RemapClassManager::remap_pointing(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        EventOutputQueue::FireRelativePointer::fire(ButtonStatus::makeButtons(), params.dx, params.dy);
      }
    }

    void
    remap_ScrollWheelEventCallback(Params_ScrollWheelEventCallback& params)
    {
      params.log();
      EventOutputQueue::FireScrollWheel::fire(params);

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }
  }
}
<commit_msg>improve Core::stop termination order<commit_after>#include "base.hpp"
#include "ButtonStatus.hpp"
#include "CommonData.hpp"
#include "Config.hpp"
#include "Core.hpp"
#include "RemapClass.hpp"
#include "remap.hpp"
#include "util/CallBackWrapper.hpp"
#include "util/EventInputQueue.hpp"
#include "util/EventOutputQueue.hpp"
#include "util/EventWatcher.hpp"
#include "util/GlobalLock.hpp"
#include "util/KeyboardRepeat.hpp"
#include "util/ListHookedConsumer.hpp"
#include "util/ListHookedKeyboard.hpp"
#include "util/ListHookedPointing.hpp"
#include "util/NumHeldDownKeys.hpp"
#include "util/PressDownKeys.hpp"
#include "util/TimerWrapper.hpp"
#include "RemapFunc/HoldingKeyToKey.hpp"
#include "RemapFunc/KeyOverlaidModifier.hpp"
#include "RemapFunc/PointingRelativeToScroll.hpp"
#include "VirtualKey.hpp"

#include <sys/errno.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOTimerEventSource.h>

namespace org_pqrs_KeyRemap4MacBook {
  namespace Core {
    namespace {
      IOWorkLoop* workLoop = NULL;
    }

    void
    start(void)
    {
      GlobalLock::initialize();
      CommonData::initialize();
      EventWatcher::initialize();
      PressDownKeys::initialize();
      FlagStatus::initialize();
      ButtonStatus::initialize();

      ListHookedKeyboard::instance().initialize();
      ListHookedConsumer::instance().initialize();
      ListHookedPointing::instance().initialize();

      workLoop = IOWorkLoop::workLoop();
      if (! workLoop) {
        IOLOG_ERROR("IOWorkLoop::workLoop failed\n");
      } else {
        KeyboardRepeat::initialize(*workLoop);
        EventInputQueue::initialize(*workLoop);
        VirtualKey::initialize(*workLoop);
        EventOutputQueue::initialize(*workLoop);
        RemapFunc::HoldingKeyToKey::static_initialize(*workLoop);
        RemapFunc::KeyOverlaidModifier::static_initialize(*workLoop);
        RemapFunc::PointingRelativeToScroll::static_initialize(*workLoop);
        ListHookedKeyboard::static_initialize(*workLoop);
        RemapClassManager::initialize(*workLoop);
      }

      Config::sysctl_register();
    }

    void
    stop(void)
    {
      // Destroy global lock.
      // Then, all callbacks and hooked functions become inactive.
      GlobalLock::terminate();

      // ------------------------------------------------------------
      ListHookedKeyboard::instance().terminate();
      ListHookedConsumer::instance().terminate();
      ListHookedPointing::instance().terminate();

      // ------------------------------------------------------------
      // call terminate
      Config::sysctl_unregister();

      RemapClassManager::terminate();
      KeyboardRepeat::terminate();
      EventInputQueue::terminate();
      VirtualKey::terminate();
      EventOutputQueue::terminate();
      RemapFunc::HoldingKeyToKey::static_terminate();
      RemapFunc::KeyOverlaidModifier::static_terminate();
      RemapFunc::PointingRelativeToScroll::static_terminate();
      ListHookedKeyboard::static_terminate();

      if (workLoop) {
        workLoop->release();
        workLoop = NULL;
      }

      EventWatcher::terminate();
      PressDownKeys::terminate();

      CommonData::terminate();
    }

    // ======================================================================
    bool
    IOHIKeyboard_gIOMatchedNotification_callback(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      GlobalLock::ScopedLock lk;
      if (! lk) return false;

      IOLOG_DEBUG("%s newService:%p\n", __FUNCTION__, newService);

      IOHIDevice* device = OSDynamicCast(IOHIKeyboard, newService);
      if (! device) return false;

      ListHookedKeyboard::instance().push_back(new ListHookedKeyboard::Item(device));
      ListHookedConsumer::instance().push_back(new ListHookedConsumer::Item(device));
      return true;
    }

    bool
    IOHIKeyboard_gIOTerminatedNotification_callback(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      GlobalLock::ScopedLock lk;
      if (! lk) return false;

      IOLOG_DEBUG("%s newService:%p\n", __FUNCTION__, newService);

      IOHIDevice* device = OSDynamicCast(IOHIKeyboard, newService);
      if (! device) return false;

      ListHookedKeyboard::instance().erase(device);
      ListHookedConsumer::instance().erase(device);
      return true;
    }

    bool
    IOHIPointing_gIOMatchedNotification_callback(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      GlobalLock::ScopedLock lk;
      if (! lk) return false;

      IOLOG_DEBUG("%s newService:%p\n", __FUNCTION__, newService);

      IOHIDevice* device = OSDynamicCast(IOHIPointing, newService);
      if (! device) return false;

      ListHookedPointing::instance().push_back(new ListHookedPointing::Item(device));
      return true;
    }

    bool
    IOHIPointing_gIOTerminatedNotification_callback(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      GlobalLock::ScopedLock lk;
      if (! lk) return false;

      IOLOG_DEBUG("%s newService:%p\n", __FUNCTION__, newService);

      IOHIDevice* device = OSDynamicCast(IOHIPointing, newService);
      if (! device) return false;

      ListHookedPointing::instance().erase(device);
      return true;
    }

    // ======================================================================
    void
    remap_KeyboardEventCallback(Params_KeyboardEventCallBack& params)
    {
      params.log();

      // ------------------------------------------------------------
      RemapParams remapParams(params);

      // ------------------------------------------------------------
      FlagStatus::set(params.key, params.flags);

      RemapClassManager::remap_key(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(params.eventType, FlagStatus::makeFlags(), params.key,
                                                                                params.charCode, params.charSet, params.origCharCode, params.origCharSet,
                                                                                params.keyboardType, false));
        if (ptr) {
          KeyboardRepeat::set(*ptr);
          EventOutputQueue::FireKey::fire(*ptr);
        }
      }

      if (NumHeldDownKeys::iszero()) {
        NumHeldDownKeys::reset();
        KeyboardRepeat::cancel();
        EventWatcher::reset();
        FlagStatus::reset();
        ButtonStatus::reset();
        VirtualKey::reset();
        EventOutputQueue::FireModifiers::fire(FlagStatus::makeFlags());
        EventOutputQueue::FireRelativePointer::fire();
        PressDownKeys::clear();
      }

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }

    void
    remap_KeyboardSpecialEventCallback(Params_KeyboardSpecialEventCallback& params)
    {
      params.log();

      RemapConsumerParams remapParams(params);

      // ------------------------------------------------------------
      RemapClassManager::remap_consumer(remapParams);

      // ----------------------------------------
      if (! remapParams.isremapped) {
        Params_KeyboardSpecialEventCallback::auto_ptr ptr(Params_KeyboardSpecialEventCallback::alloc(params.eventType, FlagStatus::makeFlags(), params.key,
                                                                                params.flavor, params.guid, false));
        if (ptr) {
          KeyboardRepeat::set(*ptr);
          EventOutputQueue::FireConsumer::fire(*ptr);
        }
      }

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }

    void
    remap_RelativePointerEventCallback(Params_RelativePointerEventCallback& params)
    {
      params.log();

      RemapPointingParams_relative remapParams(params);

      ButtonStatus::set(params.ex_button, params.ex_isbuttondown);

      RemapClassManager::remap_pointing(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        EventOutputQueue::FireRelativePointer::fire(ButtonStatus::makeButtons(), params.dx, params.dy);
      }
    }

    void
    remap_ScrollWheelEventCallback(Params_ScrollWheelEventCallback& params)
    {
      params.log();
      EventOutputQueue::FireScrollWheel::fire(params);

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }
  }
}
<|endoftext|>


<commit_before><commit_msg>Don't export builtinId of 54 or above.<commit_after><|endoftext|>


<commit_before>#include <core/tag_model.h>
#include <core/tag_item.h>

#include <QStandardItemModel>

QString TagModel::ALL = "<ALL>";
QString TagModel::UNTAGGED = "<UNTAGGED>";

TagModel::TagModel(
        QObject *parent
    )
{
    QStandardItem* header = new QStandardItem( "Tag Labels" );
    header->setTextAlignment( Qt::AlignCenter );

    model_ = new QStandardItemModel( parent );
    model_->setHorizontalHeaderItem( 0, header );

    QStandardItem* root = model_->invisibleRootItem();

    // default tree has 2 labels:
    // - untagged: images that have not been tagged at all
    // - all: all images imported regardless of their tag status
    all_item_ = new TagItem( Qt::transparent, ALL );
    untagged_item_ = new TagItem( Qt::transparent, UNTAGGED );

    root->appendRow( untagged_item_ );
    root->appendRow( all_item_ );
}

TagModel::~TagModel()
{
}

void TagModel::import_images(
        const QFileInfoList& image_list
    )
{
    for( QFileInfoList::const_iterator img_itr = image_list.begin(); img_itr != image_list.end(); ++img_itr ) {
        const QFileInfo& fi = *img_itr;

        add_image_to_label( untagged_item_, fi );
        add_image_to_label( all_item_, fi );
    }
}

bool TagModel::add_new_label(
        const QColor& color,
        const QString& label
    )
{
    // check if label is already taken
    if( model_->findItems( label, Qt::MatchCaseSensitive, 0 ).count() > 0 ) {
        return false;
    }

    // adds the new item
    QStandardItem* root = model_->invisibleRootItem();
    TagItem* new_item = new TagItem( color, label );
    root->appendRow( new_item );

    return true;
}

void TagModel::remove_items(
        const QModelIndexList& index_list
    )
{

    QSet<QModelIndex> index_to_remove;
    for( QModelIndexList::const_iterator idx_itr = index_list.begin(); idx_itr != index_list.end(); ++idx_itr ) {
        const QModelIndex& idx = *idx_itr;
        if( !idx.isValid() ) {
            continue;
        }

        TagItem* item = dynamic_cast<TagItem*>(model_->itemFromIndex( idx ));
        if( !item || item == untagged_item_ || item == all_item_ ) {
            continue;
        }

        TagItem* parent_item = dynamic_cast<TagItem*>(item->QStandardItem::parent());
        QModelIndex parent_index = parent_item? parent_item->index() : QModelIndex();

        // unreferences relevant labels associated the item
        // if parent is ALL --> completely removes item i-e from all label it belongs to
        // if parent is UNTAGGED --> does nothing, item cannot be removed from UNTAGGED
        // if parent is another label --> unref and removes from that label only
        // if parent is root (i-e we're dealing with an image item)
        //   --> remove all items belonging to that label
        if( parent_index.isValid() ) {
            const QString& fullpath = item->fullpath();
            TagItemList& ref_label_list = image_label_ref_[ fullpath ];
            TagItemList& ref_image_list = image_image_ref_[ fullpath ];

            if( parent_item == all_item_ ) {
                while( !ref_image_list.isEmpty() ) {
                    TagItem* ref = ref_image_list.takeLast();
                    index_to_remove.insert( ref->index() );
                }
                image_label_ref_.remove( fullpath );
                image_image_ref_.remove( fullpath );

            } else if( parent_item == untagged_item_ ) {
                // the only way to remove from UNTAGGED is when the image
                // is removed from ALL
                continue;

            } else {
                // unref itself
                index_to_remove.insert( item->index() );
                ref_label_list.removeAll( parent_item );
                ref_image_list.removeAll( item );

                // check if image is not reference is any other tag
                // in that case, it is added back to UNTAGGED
                TagItem* item_as_untagged = get_tag_item( fullpath, UNTAGGED );
                if( !item_as_untagged ) {
                    add_image_to_label( untagged_item_, QFileInfo( fullpath ) );
                }
            }
        } else {
            // we only unref all items from the label
            // no need to destroy individually all the items as they will be removed
            // when the tag label is removed
            for( int r = 0; r < item->rowCount(); ++r ) {
                TagItem* image_item = dynamic_cast<TagItem*>(item->child( r ));
                if( !image_item ) {
                    // it should not happen but we never know...
                    continue;
                }
                image_label_ref_[ image_item->fullpath() ].removeAll( item );
                image_image_ref_[ image_item->fullpath() ].removeAll( image_item );

                // check if image is not reference is any other tag
                // in that case, it is added back to UNTAGGED
                TagItem* item_as_untagged = get_tag_item( image_item->fullpath(), UNTAGGED );
                if( !item_as_untagged ) {
                    add_image_to_label( untagged_item_, QFileInfo( image_item->fullpath() ) );
                }
            }
            index_to_remove.insert( item->index() );
        }
    }

    // sort indices from last to first
    // otherwise selection indexing becomes invalid
    // --> using STL - qSort and other QtAlgo are obsolete
    QModelIndexList list_to_process = index_to_remove.toList();
    std::sort( list_to_process.begin(), list_to_process.end() );

    for( int idx_itr = list_to_process.count() - 1; idx_itr >= 0; --idx_itr ) {
        const QModelIndex& idx = list_to_process.at( idx_itr );

        QStandardItem* item = model_->itemFromIndex( idx );
        if( !item ) {
            continue;
        }

        QModelIndex parent_index = item->parent()? item->parent()->index() : QModelIndex();
        model_->removeRow( idx.row(), parent_index );
    }
}

TagItem* TagModel::add_image_to_label(
        TagItem* label_item,
        const QFileInfo& image_file
    )
{
    if( !label_item || !image_file.exists() ) {
        return 0;
    }

    QString image_filename = image_file.absoluteFilePath();

    // ensure the image is not already imported
    if( image_label_ref_[ image_filename ].contains( label_item ) ) {
        return 0;
    }
    TagItem* new_item = new TagItem( label_item, image_filename );

    image_label_ref_[ image_filename ].append( label_item );
    image_image_ref_[ image_filename ].append( new_item );

    return new_item;
}

QString TagModel::get_fullpath(
        const QModelIndex& index
    ) const
{
    if( !index.isValid() ) {
        return QString::null;
    }

    TagItem* item = dynamic_cast<TagItem*>(model_->itemFromIndex( index ));
    if( !item ) {
        return QString::null;
    }

    return item->fullpath();
}

TagItem* TagModel::get_tag_item(
        const QString& fullpath,
        const QString& label
    )
{
    if( fullpath.isEmpty() || label.isEmpty() ) {
        return 0;
    }

    if( !image_image_ref_.contains( fullpath ) ) {
        return 0;
    }

    const TagItemList items = image_image_ref_[ fullpath ];
    for( TagItemList::const_iterator item_itr = items.begin(); item_itr != items.end(); ++item_itr ) {
        TagItem* image = *item_itr;

        if( image->label() == label ) {
            return image;
        }
    }

    return 0;
}

TagItem::Elements TagModel::get_element(
        const QString& fullpath,
        const QString& label
    )
{
    TagItem* image = get_tag_item( fullpath, label );
    if( !image ) {
        return TagItem::Elements();
    }

    return image->elements();
}

QList<TagItem::Elements> TagModel::get_elements(
        const QModelIndex& index
    ) const
{
    QList<TagItem::Elements> elts;

    if( !index.isValid() ) {
        return elts;
    }

    TagItem* item = dynamic_cast<TagItem*>(model_->itemFromIndex( index ));
    if( !item ) {
        return elts;
    }

    // if item is in ALL, returns all the elements of all
    // labels it belongs to
    if( item->QStandardItem::parent() == all_item_ ) {
        const TagItemList& all_items = image_image_ref_[ item->fullpath() ];
        for( TagItemList::const_iterator tag_itr = all_items.begin(); tag_itr != all_items.end(); ++tag_itr ) {
            elts.append( (*tag_itr)->elements() );
        }

    } else {
        elts.append( item->elements() );

    }

    return elts;
}

QList<TagItem::Elements> TagModel::get_all_tags() const
{
    QList<TagItem::Elements> tags;
    for( int r = 0; r < model_->rowCount(); ++r ) {
        TagItem* item = dynamic_cast<TagItem*>(model_->item( r ));
        if( !item || item == untagged_item_ || item == all_item_ ) {
            continue;
        }

        tags.append( item->elements() );
    }

    return tags;
}

void TagModel::add_tag_to_label(
    const QString& fullpath,
    const QString& label,
    const QRect& tag
)
{
    TagItem* image = get_tag_item( fullpath, label );

    // it is the first tag for this label
    if( !image ) {

        QList<QStandardItem*> labels = model_->findItems( label, Qt::MatchCaseSensitive, 0 );
        if( labels.count() != 1 ) {
            // it shouldn't be possible but we never know...
            return;
        }

        image = add_image_to_label( dynamic_cast<TagItem*>(labels.first()), QFileInfo( fullpath ) );
        if( !image ) {
            return;
        }
    }

    image->add_tag( tag );

    // first tag for any label --> remove it from untagged
    TagItem* item_as_untagged = get_tag_item( fullpath, UNTAGGED );
    if( item_as_untagged ) {
        image_label_ref_[ fullpath ].removeAll( untagged_item_ );
        image_image_ref_[ fullpath ].removeAll( item_as_untagged );
        model_->removeRow( item_as_untagged->index().row(), untagged_item_->index() );
    }
}
<commit_msg>- bug fix: improper flagging of items to be removed/added back to untagged<commit_after>#include <core/tag_model.h>
#include <core/tag_item.h>

#include <QStandardItemModel>

QString TagModel::ALL = "<ALL>";
QString TagModel::UNTAGGED = "<UNTAGGED>";

TagModel::TagModel(
        QObject *parent
    )
{
    QStandardItem* header = new QStandardItem( "Tag Labels" );
    header->setTextAlignment( Qt::AlignCenter );

    model_ = new QStandardItemModel( parent );
    model_->setHorizontalHeaderItem( 0, header );

    QStandardItem* root = model_->invisibleRootItem();

    // default tree has 2 labels:
    // - untagged: images that have not been tagged at all
    // - all: all images imported regardless of their tag status
    all_item_ = new TagItem( Qt::transparent, ALL );
    untagged_item_ = new TagItem( Qt::transparent, UNTAGGED );

    root->appendRow( untagged_item_ );
    root->appendRow( all_item_ );
}

TagModel::~TagModel()
{
}

void TagModel::import_images(
        const QFileInfoList& image_list
    )
{
    for( QFileInfoList::const_iterator img_itr = image_list.begin(); img_itr != image_list.end(); ++img_itr ) {
        const QFileInfo& fi = *img_itr;

        add_image_to_label( untagged_item_, fi );
        add_image_to_label( all_item_, fi );
    }
}

bool TagModel::add_new_label(
        const QColor& color,
        const QString& label
    )
{
    // check if label is already taken
    if( model_->findItems( label, Qt::MatchCaseSensitive, 0 ).count() > 0 ) {
        return false;
    }

    // adds the new item
    QStandardItem* root = model_->invisibleRootItem();
    TagItem* new_item = new TagItem( color, label );
    root->appendRow( new_item );

    return true;
}

void TagModel::remove_items(
        const QModelIndexList& index_list
    )
{

    QSet<QModelIndex> index_to_remove;
    for( QModelIndexList::const_iterator idx_itr = index_list.begin(); idx_itr != index_list.end(); ++idx_itr ) {
        const QModelIndex& idx = *idx_itr;
        if( !idx.isValid() ) {
            continue;
        }

        TagItem* item = dynamic_cast<TagItem*>(model_->itemFromIndex( idx ));
        if( !item || item == untagged_item_ || item == all_item_ ) {
            continue;
        }

        TagItem* parent_item = dynamic_cast<TagItem*>(item->QStandardItem::parent());
        QModelIndex parent_index = parent_item? parent_item->index() : QModelIndex();

        // unreferences relevant labels associated the item
        // if parent is ALL --> completely removes item i-e from all label it belongs to
        // if parent is UNTAGGED --> does nothing, item cannot be removed from UNTAGGED
        // if parent is another label --> unref and removes from that label only
        // if parent is root (i-e we're dealing with an image item)
        //   --> remove all items belonging to that label
        if( parent_index.isValid() ) {
            const QString& fullpath = item->fullpath();
            TagItemList& ref_label_list = image_label_ref_[ fullpath ];
            TagItemList& ref_image_list = image_image_ref_[ fullpath ];

            if( parent_item == all_item_ ) {
                while( !ref_image_list.isEmpty() ) {
                    TagItem* ref = ref_image_list.takeLast();
                    index_to_remove.insert( ref->index() );
                }
                image_label_ref_.remove( fullpath );
                image_image_ref_.remove( fullpath );

            } else if( parent_item == untagged_item_ ) {
                // the only way to remove from UNTAGGED is when the image
                // is removed from ALL
                continue;

            } else {
                // unref itself
                index_to_remove.insert( item->index() );
                ref_label_list.removeAll( parent_item );
                ref_image_list.removeAll( item );

                // check if image is not reference is any other tag
                // in that case, it is added back to UNTAGGED
                TagItem* item_as_untagged = get_tag_item( fullpath, UNTAGGED );
                if( !item_as_untagged && image_label_ref_[ fullpath ].count() == 1 ) {
                    add_image_to_label( untagged_item_, QFileInfo( fullpath ) );
                }
            }
        } else {
            // we only unref all items from the label
            // no need to destroy individually all the items as they will be removed
            // when the tag label is removed
            for( int r = 0; r < item->rowCount(); ++r ) {
                TagItem* image_item = dynamic_cast<TagItem*>(item->child( r ));
                if( !image_item ) {
                    // it should not happen but we never know...
                    continue;
                }

                QString image_fullpath = image_item->fullpath();
                image_label_ref_[ image_fullpath ].removeAll( item );
                image_image_ref_[ image_fullpath ].removeAll( image_item );

                // check if image is not reference is any other tag
                // in that case, it is added back to UNTAGGED
                TagItem* item_as_untagged = get_tag_item( image_fullpath, UNTAGGED );
                if( !item_as_untagged && image_label_ref_[ image_fullpath ].count() == 1 ) {
                    add_image_to_label( untagged_item_, QFileInfo( image_fullpath ) );
                }
            }
            index_to_remove.insert( item->index() );
        }
    }

    // sort indices from last to first
    // otherwise selection indexing becomes invalid
    // --> using STL - qSort and other QtAlgo are obsolete
    QModelIndexList list_to_process = index_to_remove.toList();
    std::sort( list_to_process.begin(), list_to_process.end() );

    for( int idx_itr = list_to_process.count() - 1; idx_itr >= 0; --idx_itr ) {
        const QModelIndex& idx = list_to_process.at( idx_itr );

        QStandardItem* item = model_->itemFromIndex( idx );
        if( !item ) {
            continue;
        }

        QModelIndex parent_index = item->parent()? item->parent()->index() : QModelIndex();
        model_->removeRow( idx.row(), parent_index );
    }
}

TagItem* TagModel::add_image_to_label(
        TagItem* label_item,
        const QFileInfo& image_file
    )
{
    if( !label_item || !image_file.exists() ) {
        return 0;
    }

    QString image_filename = image_file.absoluteFilePath();

    // ensure the image is not already imported
    if( image_label_ref_[ image_filename ].contains( label_item ) ) {
        return 0;
    }
    TagItem* new_item = new TagItem( label_item, image_filename );

    image_label_ref_[ image_filename ].append( label_item );
    image_image_ref_[ image_filename ].append( new_item );

    return new_item;
}

QString TagModel::get_fullpath(
        const QModelIndex& index
    ) const
{
    if( !index.isValid() ) {
        return QString::null;
    }

    TagItem* item = dynamic_cast<TagItem*>(model_->itemFromIndex( index ));
    if( !item ) {
        return QString::null;
    }

    return item->fullpath();
}

TagItem* TagModel::get_tag_item(
        const QString& fullpath,
        const QString& label
    )
{
    if( fullpath.isEmpty() || label.isEmpty() ) {
        return 0;
    }

    if( !image_image_ref_.contains( fullpath ) ) {
        return 0;
    }

    const TagItemList items = image_image_ref_[ fullpath ];
    for( TagItemList::const_iterator item_itr = items.begin(); item_itr != items.end(); ++item_itr ) {
        TagItem* image = *item_itr;

        if( image->label() == label ) {
            return image;
        }
    }

    return 0;
}

TagItem::Elements TagModel::get_element(
        const QString& fullpath,
        const QString& label
    )
{
    TagItem* image = get_tag_item( fullpath, label );
    if( !image ) {
        return TagItem::Elements();
    }

    return image->elements();
}

QList<TagItem::Elements> TagModel::get_elements(
        const QModelIndex& index
    ) const
{
    QList<TagItem::Elements> elts;

    if( !index.isValid() ) {
        return elts;
    }

    TagItem* item = dynamic_cast<TagItem*>(model_->itemFromIndex( index ));
    if( !item ) {
        return elts;
    }

    // if item is in ALL, returns all the elements of all
    // labels it belongs to
    if( item->QStandardItem::parent() == all_item_ ) {
        const TagItemList& all_items = image_image_ref_[ item->fullpath() ];
        for( TagItemList::const_iterator tag_itr = all_items.begin(); tag_itr != all_items.end(); ++tag_itr ) {
            elts.append( (*tag_itr)->elements() );
        }

    } else {
        elts.append( item->elements() );

    }

    return elts;
}

QList<TagItem::Elements> TagModel::get_all_tags() const
{
    QList<TagItem::Elements> tags;
    for( int r = 0; r < model_->rowCount(); ++r ) {
        TagItem* item = dynamic_cast<TagItem*>(model_->item( r ));
        if( !item || item == untagged_item_ || item == all_item_ ) {
            continue;
        }

        tags.append( item->elements() );
    }

    return tags;
}

void TagModel::add_tag_to_label(
    const QString& fullpath,
    const QString& label,
    const QRect& tag
)
{
    TagItem* image = get_tag_item( fullpath, label );

    // it is the first tag for this label
    if( !image ) {

        QList<QStandardItem*> labels = model_->findItems( label, Qt::MatchCaseSensitive, 0 );
        if( labels.count() != 1 ) {
            // it shouldn't be possible but we never know...
            return;
        }

        image = add_image_to_label( dynamic_cast<TagItem*>(labels.first()), QFileInfo( fullpath ) );
        if( !image ) {
            return;
        }
    }

    image->add_tag( tag );

    // first tag for any label --> remove it from untagged
    TagItem* item_as_untagged = get_tag_item( fullpath, UNTAGGED );
    if( item_as_untagged ) {
        image_label_ref_[ fullpath ].removeAll( untagged_item_ );
        image_image_ref_[ fullpath ].removeAll( item_as_untagged );
        model_->removeRow( item_as_untagged->index().row(), untagged_item_->index() );
    }
}
<|endoftext|>


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

#include "scanner/video/decoder_automata.h"
#include "scanner/metadata.pb.h"

#include "scanner/util/h264.h"
#include "scanner/util/memory.h"

#include <thread>

namespace scanner {
namespace internal {

DecoderAutomata::DecoderAutomata(DeviceHandle device_handle, i32 num_devices,
                                 VideoDecoderType decoder_type)
    : device_handle_(device_handle), num_devices_(num_devices),
      decoder_type_(decoder_type),
      decoder_(VideoDecoder::make_from_config(device_handle, num_devices,
                                              decoder_type)),
      feeder_waiting_(false), not_done_(true), frames_retrieved_(0) {
  feeder_thread_ = std::thread(&DecoderAutomata::feeder, this);
}

DecoderAutomata::~DecoderAutomata() {
  {
    frames_to_get_ = 0;
    frames_retrieved_ = 0;
    while (decoder_->discard_frame()) {
    }

    std::unique_lock<std::mutex> lk(feeder_mutex_);
    wake_feeder_.wait(lk, [this] { return feeder_waiting_.load(); });

    if (frames_retrieved_ > 0) {
      decoder_->feed(nullptr, 0, true);
    }
    not_done_ = false;
    feeder_waiting_ = false;
  }
  wake_feeder_.notify_one();
  feeder_thread_.join();
}

void DecoderAutomata::initialize(
    const std::vector<proto::DecodeArgs> &encoded_data) {
  assert(!encoded_data.empty());
  encoded_data_ = encoded_data;
  frame_size_ = encoded_data[0].width() * encoded_data[0].height() * 3;
  current_frame_ = encoded_data[0].start_keyframe();
  next_frame_.store(encoded_data[0].valid_frames(0), std::memory_order_release);
  retriever_data_idx_.store(0, std::memory_order_release);
  retriever_valid_idx_ = 0;
  reset_current_frame_ = -1;

  FrameInfo info;
  info.set_width(encoded_data[0].width());
  info.set_height(encoded_data[0].height());

  while (decoder_->discard_frame()) {
  }

  std::unique_lock<std::mutex> lk(feeder_mutex_);
  wake_feeder_.wait(lk, [this] { return feeder_waiting_.load(); });

  if (info_.width() != info.width()
      || info_.height() != info.height()) {
    decoder_->configure(info);
  }
  if (frames_retrieved_ > 0) {
    decoder_->feed(nullptr, 0, true);
  }

  feeder_data_idx_.store(0, std::memory_order_release);
  feeder_buffer_offset_.store(0, std::memory_order_release);
  feeder_next_keyframe_.store(encoded_data[0].keyframes(1),
                              std::memory_order_release);
  feeder_data_idx_.store(0);
  feeder_buffer_offset_.store(0);
  feeder_next_keyframe_.store(encoded_data[0].keyframes(1));
  info_ = info;
  std::atomic_thread_fence(std::memory_order_release);
  seeking_ = false;
}

void DecoderAutomata::get_frames(u8* buffer, i32 num_frames) {
  i64 total_frames_decoded = 0;
  i64 total_frames_used = 0;

  // profiler_->add_interval("decode", start, now());
  // profiler_->increment("effective_frames", total_frames_used);
  // profiler_->increment("decoded_frames", total_frames_decoded);

  auto start = now();

  // Wait until feeder is waiting
  {
    // Wait until frames are being requested
    std::unique_lock<std::mutex> lk(feeder_mutex_);
    wake_feeder_.wait(lk, [this] { return feeder_waiting_.load(); });
  }

  // Start up feeder thread
  {
    std::unique_lock<std::mutex> lk(feeder_mutex_);
    frames_retrieved_ = 0;
    frames_to_get_ = num_frames;
    feeder_waiting_ = false;
  }
  wake_feeder_.notify_one();

  while (frames_retrieved_ < frames_to_get_) {
    if (decoder_->decoded_frames_buffered() > 0) {
      // New frames
      bool more_frames = true;
      while (more_frames && frames_retrieved_ < frames_to_get_) {
        const auto &valid_frames =
            encoded_data_[retriever_data_idx_].valid_frames();
        assert(current_frame_ <= valid_frames.Get(retriever_valid_idx_));
        // printf("has buffered frames, curr %d, next %d\n",
        //        current_frame_, valid_frames.Get(retriever_valid_idx_));
        if (current_frame_ == valid_frames.Get(retriever_valid_idx_)) {
          u8 *decoded_buffer = buffer + frames_retrieved_ * frame_size_;
          more_frames = decoder_->get_frame(decoded_buffer, frame_size_);
          retriever_valid_idx_++;
          if (retriever_valid_idx_ == valid_frames.size()) {
            // Move to next decode args
            retriever_data_idx_ += 1;
            retriever_valid_idx_ = 0;
          }
          if (retriever_data_idx_ < encoded_data_.size()) {
            next_frame_.store(encoded_data_[retriever_data_idx_].valid_frames(
                                  retriever_valid_idx_),
                              std::memory_order_release);
          }
          // printf("got frame %d\n", frames_retrieved_.load());
          total_frames_used++;
          frames_retrieved_++;
        } else {
          more_frames = decoder_->discard_frame();
        }
        current_frame_++;
        if (reset_current_frame_ != -1) {
          current_frame_ = reset_current_frame_;
          reset_current_frame_ = -1;
        }
        total_frames_decoded++;
      }
    }
    std::this_thread::yield();
  }
  decoder_->wait_until_frames_copied();
}

void DecoderAutomata::feeder() {
  // printf("feeder start\n");
  i64 total_frames_fed = 0;
  i32 frames_fed = 0;
  seeking_ = false;
  while (not_done_) {
    {
      // Wait until frames are being requested
      std::unique_lock<std::mutex> lk(feeder_mutex_);
      feeder_waiting_ = true;
    }
    wake_feeder_.notify_one();

    {
      std::unique_lock<std::mutex> lk(feeder_mutex_);
      wake_feeder_.wait(lk, [this] { return !feeder_waiting_; });
    }
    std::atomic_thread_fence(std::memory_order_acquire);

    // Ignore requests to feed if we have alredy fed all data
    if (encoded_data_.size() <= feeder_data_idx_) {
      continue;
    }

    frames_fed = 0;
    bool seen_metadata = false;
    while (frames_retrieved_ < frames_to_get_) {
      // if (next_frame_ > feeder_next_keyframe_) {
      //   // Jump to the next
      // }
      i32 frames_to_wait = seeking_ ? 0 : 8;
      while (frames_retrieved_ < frames_to_get_ &&
            decoder_->decoded_frames_buffered() > frames_to_wait) {
        std::this_thread::yield();
        continue;
      }
      if (seeking_) {
        decoder_->feed(nullptr, 0, true);
        reset_current_frame_ = encoded_data_[feeder_data_idx_].keyframes(0);
        seeking_ = false;
      }
      frames_fed++;

      i32 fdi = feeder_data_idx_.load(std::memory_order_acquire);
      const u8 *encoded_buffer =
          (const u8 *)encoded_data_[fdi].mutable_encoded_video()->data();
      size_t encoded_buffer_size =
          encoded_data_[fdi].mutable_encoded_video()->size();
      i32 encoded_packet_size = 0;
      const u8 *encoded_packet = NULL;
      if (feeder_buffer_offset_ < encoded_buffer_size) {
        encoded_packet_size = *reinterpret_cast<const i32 *>(
            encoded_buffer + feeder_buffer_offset_);
        feeder_buffer_offset_ += sizeof(i32);
        encoded_packet = encoded_buffer + feeder_buffer_offset_;
        assert(encoded_packet_size < encoded_buffer_size);
        feeder_buffer_offset_ += encoded_packet_size;
        // printf("encoded packet size %d, ptr %p\n", encoded_packet_size,
        //        encoded_packet);
      }

      if (seen_metadata && encoded_packet_size > 0) {
        const u8 *start_buffer = encoded_packet;
        i32 original_size = encoded_packet_size;

        while (encoded_packet_size > 0) {
          const u8 *nal_start;
          i32 nal_size;
          next_nal(encoded_packet, encoded_packet_size, nal_start, nal_size);
          if (encoded_packet_size == 0) {
            break;
          }
          i32 nal_type = get_nal_unit_type(nal_start);
          if (is_vcl_nal(nal_type)) {
            encoded_packet = nal_start -= 3;
            encoded_packet_size = nal_size + encoded_packet_size + 3;
            break;
          }
        }
      }

      //if (encoded_packet_size != 0) {
        decoder_->feed(encoded_packet, encoded_packet_size, false);
        //}
      // Set a discontinuity if we sent an empty packet to reset
      // the stream next time
      if (encoded_packet_size == 0) {
        assert(feeder_buffer_offset_ >= encoded_buffer_size);
        // Reached the end of a decoded segment so wait for decoder to flush
        // before moving onto next segment
        seen_metadata = false;
        feeder_data_idx_ += 1;
        feeder_buffer_offset_ = 0;
        seeking_ = true;
        if (encoded_data_.size() <= feeder_data_idx_) {
          break;
        }
        feeder_next_keyframe_ = encoded_data_[feeder_data_idx_].keyframes(1);
      } else {
        seen_metadata = true;
      }
      std::this_thread::yield();
    }
  }
}
}
}
<commit_msg>Fix decoder reset frame occuring after frame reset<commit_after>/* Copyright 2016 Carnegie Mellon University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "scanner/video/decoder_automata.h"
#include "scanner/metadata.pb.h"

#include "scanner/util/h264.h"
#include "scanner/util/memory.h"

#include <thread>

namespace scanner {
namespace internal {

DecoderAutomata::DecoderAutomata(DeviceHandle device_handle, i32 num_devices,
                                 VideoDecoderType decoder_type)
    : device_handle_(device_handle), num_devices_(num_devices),
      decoder_type_(decoder_type),
      decoder_(VideoDecoder::make_from_config(device_handle, num_devices,
                                              decoder_type)),
      feeder_waiting_(false), not_done_(true), frames_retrieved_(0) {
  feeder_thread_ = std::thread(&DecoderAutomata::feeder, this);
}

DecoderAutomata::~DecoderAutomata() {
  {
    frames_to_get_ = 0;
    frames_retrieved_ = 0;
    while (decoder_->discard_frame()) {
    }

    std::unique_lock<std::mutex> lk(feeder_mutex_);
    wake_feeder_.wait(lk, [this] { return feeder_waiting_.load(); });

    if (frames_retrieved_ > 0) {
      decoder_->feed(nullptr, 0, true);
    }
    not_done_ = false;
    feeder_waiting_ = false;
  }
  wake_feeder_.notify_one();
  feeder_thread_.join();
}

void DecoderAutomata::initialize(
    const std::vector<proto::DecodeArgs> &encoded_data) {
  assert(!encoded_data.empty());
  encoded_data_ = encoded_data;
  frame_size_ = encoded_data[0].width() * encoded_data[0].height() * 3;
  current_frame_ = encoded_data[0].start_keyframe();
  next_frame_.store(encoded_data[0].valid_frames(0), std::memory_order_release);
  retriever_data_idx_.store(0, std::memory_order_release);
  retriever_valid_idx_ = 0;
  reset_current_frame_ = -1;

  FrameInfo info;
  info.set_width(encoded_data[0].width());
  info.set_height(encoded_data[0].height());

  while (decoder_->discard_frame()) {
  }

  std::unique_lock<std::mutex> lk(feeder_mutex_);
  wake_feeder_.wait(lk, [this] { return feeder_waiting_.load(); });

  if (info_.width() != info.width()
      || info_.height() != info.height()) {
    decoder_->configure(info);
  }
  if (frames_retrieved_ > 0) {
    decoder_->feed(nullptr, 0, true);
  }

  feeder_data_idx_.store(0, std::memory_order_release);
  feeder_buffer_offset_.store(0, std::memory_order_release);
  feeder_next_keyframe_.store(encoded_data[0].keyframes(1),
                              std::memory_order_release);
  feeder_data_idx_.store(0);
  feeder_buffer_offset_.store(0);
  feeder_next_keyframe_.store(encoded_data[0].keyframes(1));
  info_ = info;
  std::atomic_thread_fence(std::memory_order_release);
  seeking_ = false;
}

void DecoderAutomata::get_frames(u8* buffer, i32 num_frames) {
  i64 total_frames_decoded = 0;
  i64 total_frames_used = 0;

  // profiler_->add_interval("decode", start, now());
  // profiler_->increment("effective_frames", total_frames_used);
  // profiler_->increment("decoded_frames", total_frames_decoded);

  auto start = now();

  // Wait until feeder is waiting
  {
    // Wait until frames are being requested
    std::unique_lock<std::mutex> lk(feeder_mutex_);
    wake_feeder_.wait(lk, [this] { return feeder_waiting_.load(); });
  }

  // Start up feeder thread
  {
    std::unique_lock<std::mutex> lk(feeder_mutex_);
    frames_retrieved_ = 0;
    frames_to_get_ = num_frames;
    feeder_waiting_ = false;
  }
  wake_feeder_.notify_one();

  while (frames_retrieved_ < frames_to_get_) {
    if (reset_current_frame_ != -1) {
      printf("reset current frame\n");
      current_frame_ = reset_current_frame_;
      reset_current_frame_ = -1;
    }
    if (decoder_->decoded_frames_buffered() > 0) {
      // New frames
      bool more_frames = true;
      while (more_frames && frames_retrieved_ < frames_to_get_) {
        const auto &valid_frames =
            encoded_data_[retriever_data_idx_].valid_frames();
        assert(current_frame_ <= valid_frames.Get(retriever_valid_idx_));
        // printf("has buffered frames, curr %d, next %d\n",
        //        current_frame_, valid_frames.Get(retriever_valid_idx_));
        if (current_frame_ == valid_frames.Get(retriever_valid_idx_)) {
          u8 *decoded_buffer = buffer + frames_retrieved_ * frame_size_;
          more_frames = decoder_->get_frame(decoded_buffer, frame_size_);
          retriever_valid_idx_++;
          if (retriever_valid_idx_ == valid_frames.size()) {
            // Move to next decode args
            retriever_data_idx_ += 1;
            retriever_valid_idx_ = 0;
          }
          if (retriever_data_idx_ < encoded_data_.size()) {
            next_frame_.store(encoded_data_[retriever_data_idx_].valid_frames(
                                  retriever_valid_idx_),
                              std::memory_order_release);
          }
          // printf("got frame %d\n", frames_retrieved_.load());
          total_frames_used++;
          frames_retrieved_++;
        } else {
          more_frames = decoder_->discard_frame();
        }
        current_frame_++;
        total_frames_decoded++;
      }
    }
    std::this_thread::yield();
  }
  decoder_->wait_until_frames_copied();
}

void DecoderAutomata::feeder() {
  // printf("feeder start\n");
  i64 total_frames_fed = 0;
  i32 frames_fed = 0;
  seeking_ = false;
  while (not_done_) {
    {
      // Wait until frames are being requested
      std::unique_lock<std::mutex> lk(feeder_mutex_);
      feeder_waiting_ = true;
    }
    wake_feeder_.notify_one();

    {
      std::unique_lock<std::mutex> lk(feeder_mutex_);
      wake_feeder_.wait(lk, [this] { return !feeder_waiting_; });
    }
    std::atomic_thread_fence(std::memory_order_acquire);

    // Ignore requests to feed if we have alredy fed all data
    if (encoded_data_.size() <= feeder_data_idx_) {
      continue;
    }

    frames_fed = 0;
    bool seen_metadata = false;
    while (frames_retrieved_ < frames_to_get_) {
      // if (next_frame_ > feeder_next_keyframe_) {
      //   // Jump to the next
      // }
      i32 frames_to_wait = seeking_ ? 0 : 8;
      while (frames_retrieved_ < frames_to_get_ &&
            decoder_->decoded_frames_buffered() > frames_to_wait) {
        std::this_thread::yield();
        continue;
      }
      if (seeking_) {
        decoder_->feed(nullptr, 0, true);
        reset_current_frame_ = encoded_data_[feeder_data_idx_].keyframes(0);
        seeking_ = false;
      }
      frames_fed++;

      i32 fdi = feeder_data_idx_.load(std::memory_order_acquire);
      const u8 *encoded_buffer =
          (const u8 *)encoded_data_[fdi].mutable_encoded_video()->data();
      size_t encoded_buffer_size =
          encoded_data_[fdi].mutable_encoded_video()->size();
      i32 encoded_packet_size = 0;
      const u8 *encoded_packet = NULL;
      if (feeder_buffer_offset_ < encoded_buffer_size) {
        encoded_packet_size = *reinterpret_cast<const i32 *>(
            encoded_buffer + feeder_buffer_offset_);
        feeder_buffer_offset_ += sizeof(i32);
        encoded_packet = encoded_buffer + feeder_buffer_offset_;
        assert(encoded_packet_size < encoded_buffer_size);
        feeder_buffer_offset_ += encoded_packet_size;
        // printf("encoded packet size %d, ptr %p\n", encoded_packet_size,
        //        encoded_packet);
      }

      if (seen_metadata && encoded_packet_size > 0) {
        const u8 *start_buffer = encoded_packet;
        i32 original_size = encoded_packet_size;

        while (encoded_packet_size > 0) {
          const u8 *nal_start;
          i32 nal_size;
          next_nal(encoded_packet, encoded_packet_size, nal_start, nal_size);
          if (encoded_packet_size == 0) {
            break;
          }
          i32 nal_type = get_nal_unit_type(nal_start);
          if (is_vcl_nal(nal_type)) {
            encoded_packet = nal_start -= 3;
            encoded_packet_size = nal_size + encoded_packet_size + 3;
            break;
          }
        }
      }

      //if (encoded_packet_size != 0) {
        decoder_->feed(encoded_packet, encoded_packet_size, false);
        //}
      // Set a discontinuity if we sent an empty packet to reset
      // the stream next time
      if (encoded_packet_size == 0) {
        assert(feeder_buffer_offset_ >= encoded_buffer_size);
        // Reached the end of a decoded segment so wait for decoder to flush
        // before moving onto next segment
        seen_metadata = false;
        feeder_data_idx_ += 1;
        feeder_buffer_offset_ = 0;
        seeking_ = true;
        if (encoded_data_.size() <= feeder_data_idx_) {
          break;
        }
        feeder_next_keyframe_ = encoded_data_[feeder_data_idx_].keyframes(1);
      } else {
        seen_metadata = true;
      }
      std::this_thread::yield();
    }
  }
}
}
}
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// ddl_utils.cpp
//
// Identification: src/backend/bridge/ddl/ddl_utils.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <cassert>
#include <iostream>

#include "backend/bridge/ddl/ddl_utils.h"
#include "backend/bridge/ddl/format_transformer.h"
#include "backend/bridge/ddl/bridge.h"
#include "backend/common/logger.h"
#include "backend/storage/database.h"

#include "postmaster/peloton.h"
#include "parser/parse_type.h"
#include "utils/syscache.h"
#include "miscadmin.h"
#include "parser/parse_utilcmd.h"
#include "access/htup_details.h"
#include "utils/resowner.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"

namespace peloton {
namespace bridge {

//===--------------------------------------------------------------------===//
// DDL Utils
//===--------------------------------------------------------------------===//

/**
 * @brief setting default constraint
 */
void DDLUtils::SetDefaultConstraint(ColumnDef *coldef, int column_itr,
                                    oid_t relation_oid) {
  Relation relation = heap_open(relation_oid, AccessShareLock);
  int num_defva = relation->rd_att->constr->num_defval;
  for (int def_itr = 0; def_itr < num_defva; def_itr++) {
    if (column_itr == relation->rd_att->constr->defval[def_itr].adnum) {
      char *default_expression =
          relation->rd_att->constr->defval[def_itr].adbin;
      coldef->cooked_default =
          static_cast<Node *>(stringToNode(default_expression));
    }
  }
  heap_close(relation, AccessShareLock);
}

/**
 * @brief parsing create statement
 * @param Cstmt a create statement
 * @param column_infos to create a table
 * @param refernce_table_infos to store reference table to the table
 */
void DDLUtils::ParsingCreateStmt(

    CreateStmt *Cstmt, std::vector<catalog::Column> &column_infos){

  assert(Cstmt);

  //===--------------------------------------------------------------------===//
  // Column Infomation
  //===--------------------------------------------------------------------===//

  // Get the column list from the create statement
  List *ColumnList = (List *)(Cstmt->tableElts);

  // Parse the CreateStmt and construct ColumnInfo
  ListCell *entry;
  foreach (entry, ColumnList) {
    ColumnDef *coldef = static_cast<ColumnDef *>(lfirst(entry));

    // Get the type oid and type mod with given typeName
    Oid typeoid = coldef->typeName->type_oid;
    int typelen = coldef->typeName->type_len;

    PostgresValueFormat postgresValueFormat( typeoid, typelen, typelen);
    PelotonValueFormat pelotonValueFormat = FormatTransformer::TransformValueFormat(postgresValueFormat);

    ValueType column_valueType = pelotonValueFormat.GetType();
    int column_length = pelotonValueFormat.GetLength();
    bool is_inlined = pelotonValueFormat.IsInlined();

    std::string column_name = coldef->colname;

    //===--------------------------------------------------------------------===//
    // Column Constraint
    //===--------------------------------------------------------------------===//

    std::vector<catalog::Constraint> column_constraints;

    if (coldef->constraints != NULL) {
      ListCell *constNodeEntry;

      foreach (constNodeEntry, coldef->constraints) {
        Constraint *ConstraintNode =
            static_cast<Constraint *>(lfirst(constNodeEntry));
        ConstraintType contype;

        // CONSTRAINT TYPE
        contype = PostgresConstraintTypeToPelotonConstraintType(
            (PostgresConstraintType)ConstraintNode->contype);

        // CONSTRAINT NAME
        std::string conname;
        if (ConstraintNode->conname != NULL) {
          conname = std::string(ConstraintNode->conname);
        }

        switch (contype) {
          case CONSTRAINT_TYPE_UNIQUE:
          case CONSTRAINT_TYPE_FOREIGN:
            continue;

          case CONSTRAINT_TYPE_NULL:
          case CONSTRAINT_TYPE_NOTNULL:
          case CONSTRAINT_TYPE_PRIMARY: {
            catalog::Constraint constraint(contype, conname);
            column_constraints.push_back(constraint);
            break;
          }
          case CONSTRAINT_TYPE_CHECK: {
            catalog::Constraint constraint(contype, conname,
                                           ConstraintNode->raw_expr);
            column_constraints.push_back(constraint);
            break;
          }
          case CONSTRAINT_TYPE_DEFAULT: {
            catalog::Constraint constraint(contype, conname,
                                           coldef->cooked_default);
            column_constraints.push_back(constraint);
            break;
          }
          default: {
            LOG_WARN("Unrecognized constraint type %d\n", (int)contype);
            break;
          }
        }
      }
    }  // end of parsing constraint

    catalog::Column column_info(column_valueType, column_length, column_name,
                                is_inlined);

    for (auto constraint : column_constraints)
      column_info.AddConstraint(constraint);

    // Insert column_info into ColumnInfos
    column_infos.push_back(column_info);
  }  // end of parsing column list
}

}  // namespace bridge
}  // namespace peloton
<commit_msg>Fix bug<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// ddl_utils.cpp
//
// Identification: src/backend/bridge/ddl/ddl_utils.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <cassert>
#include <iostream>

#include "backend/bridge/ddl/ddl_utils.h"
#include "backend/bridge/ddl/format_transformer.h"
#include "backend/bridge/ddl/bridge.h"
#include "backend/common/logger.h"
#include "backend/storage/database.h"

#include "postmaster/peloton.h"
#include "parser/parse_type.h"
#include "utils/syscache.h"
#include "miscadmin.h"
#include "parser/parse_utilcmd.h"
#include "access/htup_details.h"
#include "utils/resowner.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"

namespace peloton {
namespace bridge {

//===--------------------------------------------------------------------===//
// DDL Utils
//===--------------------------------------------------------------------===//

/**
 * @brief setting default constraint
 */
void DDLUtils::SetDefaultConstraint(ColumnDef *coldef, int column_itr,
                                    oid_t relation_oid) {
  Relation relation = heap_open(relation_oid, AccessShareLock);
  int num_defva = relation->rd_att->constr->num_defval;
  for (int def_itr = 0; def_itr < num_defva; def_itr++) {
    if (column_itr == relation->rd_att->constr->defval[def_itr].adnum) {
      char *default_expression =
          relation->rd_att->constr->defval[def_itr].adbin;
      coldef->cooked_default =
          static_cast<Node *>(stringToNode(default_expression));
    }
  }
  heap_close(relation, AccessShareLock);
}

/**
 * @brief parsing create statement
 * @param Cstmt a create statement
 * @param column_infos to create a table
 * @param refernce_table_infos to store reference table to the table
 */
void DDLUtils::ParsingCreateStmt(

    CreateStmt *Cstmt, std::vector<catalog::Column> &column_infos){

  assert(Cstmt);

  //===--------------------------------------------------------------------===//
  // Column Infomation
  //===--------------------------------------------------------------------===//

  // Get the column list from the create statement
  List *ColumnList = (List *)(Cstmt->tableElts);

  // Parse the CreateStmt and construct ColumnInfo
  ListCell *entry;
  foreach (entry, ColumnList) {
    ColumnDef *coldef = static_cast<ColumnDef *>(lfirst(entry));

    Oid typeoid = typenameTypeId(NULL, coldef->typeName);
    int32 typemod;
    typenameTypeIdAndMod(NULL, coldef->typeName, &typeoid, &typemod);

    // Get type length
    Type tup = typeidType(typeoid);
    int typelen = typeLen(tup);
    ReleaseSysCache(tup);

    // For a fixed-size type, typlen is the number of bytes in the internal
    // representation of the type. But for a variable-length type, typlen
    // is negative.
    if (typelen == -1) typelen = typemod;

    PostgresValueFormat postgresValueFormat(typeoid, typelen, typelen);
    PelotonValueFormat pelotonValueFormat = FormatTransformer::TransformValueFormat(postgresValueFormat);

    ValueType column_valueType = pelotonValueFormat.GetType();
    int column_length = pelotonValueFormat.GetLength();
    bool is_inlined = pelotonValueFormat.IsInlined();

    std::string column_name = coldef->colname;

    //===--------------------------------------------------------------------===//
    // Column Constraint
    //===--------------------------------------------------------------------===//

    std::vector<catalog::Constraint> column_constraints;

    if (coldef->constraints != NULL) {
      ListCell *constNodeEntry;

      foreach (constNodeEntry, coldef->constraints) {
        Constraint *ConstraintNode =
            static_cast<Constraint *>(lfirst(constNodeEntry));
        ConstraintType contype;

        // CONSTRAINT TYPE
        contype = PostgresConstraintTypeToPelotonConstraintType(
            (PostgresConstraintType)ConstraintNode->contype);

        // CONSTRAINT NAME
        std::string conname;
        if (ConstraintNode->conname != NULL) {
          conname = std::string(ConstraintNode->conname);
        }

        switch (contype) {
          case CONSTRAINT_TYPE_UNIQUE:
          case CONSTRAINT_TYPE_FOREIGN:
            continue;

          case CONSTRAINT_TYPE_NULL:
          case CONSTRAINT_TYPE_NOTNULL:
          case CONSTRAINT_TYPE_PRIMARY: {
            catalog::Constraint constraint(contype, conname);
            column_constraints.push_back(constraint);
            break;
          }
          case CONSTRAINT_TYPE_CHECK: {
            catalog::Constraint constraint(contype, conname,
                                           ConstraintNode->raw_expr);
            column_constraints.push_back(constraint);
            break;
          }
          case CONSTRAINT_TYPE_DEFAULT: {
            catalog::Constraint constraint(contype, conname,
                                           coldef->cooked_default);
            column_constraints.push_back(constraint);
            break;
          }
          default: {
            LOG_WARN("Unrecognized constraint type %d\n", (int)contype);
            break;
          }
        }
      }
    }  // end of parsing constraint

    catalog::Column column_info(column_valueType, column_length, column_name,
                                is_inlined);

    for (auto constraint : column_constraints)
      column_info.AddConstraint(constraint);

    // Insert column_info into ColumnInfos
    column_infos.push_back(column_info);
  }  // end of parsing column list
}

}  // namespace bridge
}  // namespace peloton
<|endoftext|>