sync documentation tree with current master
|
|
@ -254,8 +254,10 @@ def configurePlatform(env):
|
||||||
problems.append('We need boost::format (header).')
|
problems.append('We need boost::format (header).')
|
||||||
if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'):
|
if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'):
|
||||||
problems.append('We need boost::program_options (including binary lib for linking).')
|
problems.append('We need boost::program_options (including binary lib for linking).')
|
||||||
|
if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'):
|
||||||
|
problems.append('We need the boost::system support library (including binary lib).')
|
||||||
if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'):
|
if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'):
|
||||||
problems.append('We need the boost::filesystem (including binary lib for linking).')
|
problems.append('We need the boost::filesystem lib (including binary lib for linking).')
|
||||||
if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'):
|
if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'):
|
||||||
problems.append('We need the boost regular expression lib (incl. binary lib for linking).')
|
problems.append('We need the boost regular expression lib (incl. binary lib for linking).')
|
||||||
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
BIN
doc/devel/uml/fig143877.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
doc/devel/uml/fig144005.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
doc/devel/uml/fig145157.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Trafo - transforming processing Node
|
SchedulerFrontend - access point to the scheduler within the renderengine
|
||||||
|
|
||||||
Copyright (C) Lumiera.org
|
Copyright (C) Lumiera.org
|
||||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU General Public License as
|
modify it under the terms of the GNU General Public License as
|
||||||
|
|
@ -21,13 +21,14 @@
|
||||||
* *****************************************************/
|
* *****************************************************/
|
||||||
|
|
||||||
|
|
||||||
#include "proc/engine/trafo.hpp"
|
#include "backend/engine/scheduler-frontend.hpp"
|
||||||
|
|
||||||
|
namespace backend{
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
namespace engine
|
|
||||||
{
|
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace engine
|
}} // namespace backend::engine
|
||||||
55
src/backend/engine/scheduler-frontend.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
SCHEDULER-FRONTEND.hpp - access point to the scheduler within the renderengine
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BACKEND_ENGINE_SCHEDULER_FRONTEND_H
|
||||||
|
#define BACKEND_ENGINE_SCHEDULER_FRONTEND_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//using std::list;
|
||||||
|
|
||||||
|
|
||||||
|
namespace backend{
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo this is planned to become the frontend
|
||||||
|
* to the render node network, which can be considered
|
||||||
|
* at the lower end of the middle layer; the actual
|
||||||
|
* render operations are mostly implemented by the backend
|
||||||
|
* ////////TODO WIP as of 12/2010
|
||||||
|
*/
|
||||||
|
class SchedulerFrontend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///// TODO: find out about the public operations
|
||||||
|
// note: the play controller lives in the proc-layer,
|
||||||
|
// but is a subsystem separate of the sesison.
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}} // namespace backend::engine
|
||||||
|
#endif
|
||||||
|
|
@ -200,7 +200,7 @@ namespace backend {
|
||||||
|
|
||||||
|
|
||||||
/** Synchronisation barrier. In the function executing in this thread
|
/** Synchronisation barrier. In the function executing in this thread
|
||||||
* needs to be a corresponding Thread::sync() call. Blocking until
|
* needs to be a corresponding Thread::syncPoint() call. Blocking until
|
||||||
* both the caller and the thread have reached the barrier.
|
* both the caller and the thread have reached the barrier.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
|
|
|
||||||
123
src/common/interface-facade-link.hpp
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
INTERFACE-FACADE-LINK - a switchable link from interface to service implementation
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file interface-facade-link.hpp
|
||||||
|
** Opening, accessing and closing the service access through a facade interface.
|
||||||
|
** Client code is assumed to access an application level service through an facade
|
||||||
|
** interface, while the actual implementation object remains an opaque internal detail.
|
||||||
|
** Moreover, services may come up and shut down, so the implementation might change
|
||||||
|
** during the Lifecycle. The facility defined here in this header provides a basic
|
||||||
|
** implementation for this access mechanism, but without any adaptation, binding
|
||||||
|
** or plugin access layer. It works only under the assumption that both the
|
||||||
|
** interface and the actual service implementation coexist in the same
|
||||||
|
** executable and are written in C++, so any invocation of an
|
||||||
|
** interface method boils down to a language-level call.
|
||||||
|
**
|
||||||
|
** Usually, client code doesn't need to include this header. Clients are assumed
|
||||||
|
** to use the facade interface of the service in question. This facade interface
|
||||||
|
** contains a static member of type \c lumiera::facade::Accessor<I> (where I is
|
||||||
|
** the type of the facade interface). The Accessor baseclass is defined in
|
||||||
|
** interfaceproxy.hpp and typically included through the facade header.
|
||||||
|
**
|
||||||
|
** @note there is a way more elaborate implementation of the same mechanism
|
||||||
|
** for use with the Lumiera Interface/Plugin system.
|
||||||
|
**
|
||||||
|
** @see interfaceproxy.hpp description of the more general use case
|
||||||
|
** @see PlayService example for the simple use case
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
|
||||||
|
#define LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "lib/test/test-helper.hpp"
|
||||||
|
#include "include/interfaceproxy.hpp"
|
||||||
|
#include "lib/symbol.hpp"
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace lumiera {
|
||||||
|
namespace facade {
|
||||||
|
|
||||||
|
using lib::Literal;
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* simple access-frontend to the implementation of a service (C++ only).
|
||||||
|
* Usually, an instance of Accessor is placed as static member right into
|
||||||
|
* the facade interface used to access the service. This implementation
|
||||||
|
* of the access mechanism handles the simple case that both the facade
|
||||||
|
* and the service implementation are written in C++ and calls happen
|
||||||
|
* within the main executable as direct language calls, without an
|
||||||
|
* binding layer and without involving the Interface/Plugin system.
|
||||||
|
*
|
||||||
|
* Typically, the InterfaceFacadeLink becomes a member of the service
|
||||||
|
* implementation class and is directly tied into the constructor of
|
||||||
|
* the latter. Being a subclass of lumiera::facade::Accessor, it is
|
||||||
|
* allowed to "open" the facade access just by setting the static
|
||||||
|
* protected pointer Accessor::implProxy_
|
||||||
|
*/
|
||||||
|
template<class FA>
|
||||||
|
class InterfaceFacadeLink
|
||||||
|
: protected Accessor<FA>
|
||||||
|
, boost::noncopyable
|
||||||
|
{
|
||||||
|
Literal displayName_;
|
||||||
|
|
||||||
|
void
|
||||||
|
__checkLifecycle ()
|
||||||
|
{
|
||||||
|
if (Accessor<FA>::implProxy_)
|
||||||
|
throw error::State("Attempt to open an already opened Facade interface."
|
||||||
|
, error::LUMIERA_ERROR_LIFECYCLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterfaceFacadeLink(FA& serviceImpl, Literal interfaceName_for_Log=0)
|
||||||
|
: displayName_(lib::test::showType<FA>(interfaceName_for_Log))
|
||||||
|
{
|
||||||
|
__checkLifecycle();
|
||||||
|
Accessor<FA>::implProxy_ = &serviceImpl;
|
||||||
|
INFO (interface, "interface %s opened", displayName_.c());
|
||||||
|
}
|
||||||
|
|
||||||
|
~InterfaceFacadeLink()
|
||||||
|
{
|
||||||
|
INFO (interface, "closing interface %s...", displayName_.c());
|
||||||
|
Accessor<FA>::implProxy_ = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** storage for the static access pointer */
|
||||||
|
template<class FA>
|
||||||
|
FA* Accessor<FA>::implProxy_;
|
||||||
|
|
||||||
|
|
||||||
|
}} // namespace lumiera::facade
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -32,16 +32,32 @@ namespace lumiera {
|
||||||
namespace facade {
|
namespace facade {
|
||||||
|
|
||||||
|
|
||||||
LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible");
|
LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade is closed; service currently not accessible");
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation Base
|
||||||
|
* for building Facade Proxy implementations.
|
||||||
|
* Typically the purpose of such a proxy is to route
|
||||||
|
* any calls through the C-Bindings of the Lumiera Interface system.
|
||||||
|
* The actual storage for the concrete proxy object is embedded,
|
||||||
|
* inline within the #open() function. For access by the clients,
|
||||||
|
* a frontend-object of type \c Accessor<FA> may be placed into
|
||||||
|
* the facade interface; this accessor-frontend is basically
|
||||||
|
* a concealed static pointer to the proxy, and will be set,
|
||||||
|
* when the interface is opened. This opening and closing
|
||||||
|
* of the interface itself is controlled by the
|
||||||
|
* InstanceHandle, which in turn is typically
|
||||||
|
* created and managed within the context
|
||||||
|
* of the service implementation.
|
||||||
|
*/
|
||||||
template<class IHA>
|
template<class IHA>
|
||||||
class Holder;
|
class Holder;
|
||||||
|
|
||||||
template<class FA, class I>
|
template<class FA, class I>
|
||||||
class Holder<InstanceHandle<I,FA> >
|
class Holder<InstanceHandle<I,FA> >
|
||||||
: Accessor<FA>,
|
: Accessor<FA>
|
||||||
protected FA
|
, protected FA
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
typedef InstanceHandle<I,FA> IHandle;
|
typedef InstanceHandle<I,FA> IHandle;
|
||||||
|
|
@ -92,9 +108,7 @@ namespace lumiera {
|
||||||
Proxy<IHA>::close();
|
Proxy<IHA>::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace facade
|
}} // namespace lumiera::facade
|
||||||
|
|
||||||
} // namespace lumiera
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,10 @@ namespace lumiera {
|
||||||
|
|
||||||
|
|
||||||
/** initiate termination of this subsystem.
|
/** initiate termination of this subsystem.
|
||||||
* may be called repeatedly any time...
|
* This trigger may be called repeatedly any time...
|
||||||
|
* When the subsystem actually has terminated,
|
||||||
|
* the SigTerm passed to #start must be invoked.
|
||||||
|
* @note called within a locked context (barrier)
|
||||||
* @warning must not block nor throw. */
|
* @warning must not block nor throw. */
|
||||||
virtual void triggerShutdown () throw() =0;
|
virtual void triggerShutdown () throw() =0;
|
||||||
|
|
||||||
|
|
@ -114,7 +117,7 @@ namespace lumiera {
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** weather this subsystem is actually operational.
|
/** whether this subsystem is actually operational.
|
||||||
* When returning \c false here, the application may
|
* When returning \c false here, the application may
|
||||||
* terminate at any point without further notice
|
* terminate at any point without further notice
|
||||||
* Note further, that a subsystem must not be in
|
* Note further, that a subsystem must not be in
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ namespace lumiera {
|
||||||
if (!and_all (susy->getPrerequisites(), isRunning() ))
|
if (!and_all (susy->getPrerequisites(), isRunning() ))
|
||||||
{
|
{
|
||||||
susy->triggerShutdown();
|
susy->triggerShutdown();
|
||||||
throw error::Logic("Unable to start all prerequisites of Subsystem "+string(*susy));
|
throw error::State("Unable to start all prerequisites of Subsystem "+string(*susy));
|
||||||
} }
|
} }
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
#include "common/instancehandle.hpp"
|
#include "common/instancehandle.hpp"
|
||||||
#include "lib/singleton-ref.hpp"
|
#include "lib/singleton-ref.hpp"
|
||||||
#include "lib/scoped-ptrvect.hpp"
|
#include "lib/scoped-ptrvect.hpp"
|
||||||
|
#include "include/logging.h"
|
||||||
|
|
||||||
#include <glibmm.h>
|
#include <glibmm.h>
|
||||||
#include <sigc++/sigc++.h>
|
#include <sigc++/sigc++.h>
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,12 @@
|
||||||
|
|
||||||
#include "gui/gtk-lumiera.hpp"
|
#include "gui/gtk-lumiera.hpp"
|
||||||
#include "gui/panels/timeline-panel.hpp"
|
#include "gui/panels/timeline-panel.hpp"
|
||||||
|
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
|
||||||
|
|
||||||
#include "gui/workspace/workspace-window.hpp"
|
#include "gui/workspace/workspace-window.hpp"
|
||||||
#include "gui/model/project.hpp"
|
#include "gui/model/project.hpp"
|
||||||
#include "gui/controller/controller.hpp"
|
#include "gui/controller/controller.hpp"
|
||||||
|
|
||||||
#include "lib/util.hpp"
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
@ -34,6 +36,7 @@
|
||||||
using namespace Gtk;
|
using namespace Gtk;
|
||||||
using namespace sigc;
|
using namespace sigc;
|
||||||
using namespace gui::widgets;
|
using namespace gui::widgets;
|
||||||
|
using namespace gui::widgets::timeline;
|
||||||
using namespace gui::model;
|
using namespace gui::model;
|
||||||
|
|
||||||
using boost::shared_ptr; ///////////////////////////////TICKET #796
|
using boost::shared_ptr; ///////////////////////////////TICKET #796
|
||||||
|
|
@ -60,6 +63,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
||||||
, iBeamTool(Gtk::StockID("tool_i_beam"))
|
, iBeamTool(Gtk::StockID("tool_i_beam"))
|
||||||
, zoomIn(Stock::ZOOM_IN)
|
, zoomIn(Stock::ZOOM_IN)
|
||||||
, zoomOut(Stock::ZOOM_OUT)
|
, zoomOut(Stock::ZOOM_OUT)
|
||||||
|
, zoomScale()
|
||||||
, updatingToolbar(false)
|
, updatingToolbar(false)
|
||||||
, currentTool(timeline::Arrow)
|
, currentTool(timeline::Arrow)
|
||||||
{
|
{
|
||||||
|
|
@ -101,8 +105,9 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
||||||
|
|
||||||
toolbar.append(separator2);
|
toolbar.append(separator2);
|
||||||
|
|
||||||
toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in));
|
toolbar.append(zoomScale);
|
||||||
toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out));
|
zoomScale.signal_zoom().
|
||||||
|
connect(mem_fun(this,&TimelinePanel::on_zoom));
|
||||||
|
|
||||||
toolbar.show_all();
|
toolbar.show_all();
|
||||||
panelBar.pack_start(toolbar, PACK_SHRINK);
|
panelBar.pack_start(toolbar, PACK_SHRINK);
|
||||||
|
|
@ -122,13 +127,19 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
||||||
|
|
||||||
zoomIn .set_tooltip_text(_("Zoom in"));
|
zoomIn .set_tooltip_text(_("Zoom in"));
|
||||||
zoomOut .set_tooltip_text(_("Zoom out"));
|
zoomOut .set_tooltip_text(_("Zoom out"));
|
||||||
|
zoomScale .set_tooltip_text(_("Adjust timeline zoom scale"));
|
||||||
|
|
||||||
// Setup the timeline widget
|
// Setup the timeline widget
|
||||||
shared_ptr<Sequence> sequence ///////////////////////////////TICKET #796
|
shared_ptr<Sequence> sequence ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr instead of boost
|
||||||
= *get_project().get_sequences().begin();
|
= *get_project().get_sequences().begin();
|
||||||
timelineWidget.reset(new TimelineWidget(load_state(sequence)));
|
timelineWidget.reset(new TimelineWidget(load_state(sequence)));
|
||||||
pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
|
pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
|
||||||
|
|
||||||
|
// since TimelineWidget is now initialised,
|
||||||
|
// wire the zoom slider to react on timeline state changes
|
||||||
|
zoomScale.wireTimelineState (timelineWidget->get_state(),
|
||||||
|
timelineWidget->state_changed_signal());
|
||||||
|
|
||||||
// Set the initial UI state
|
// Set the initial UI state
|
||||||
update_sequence_chooser();
|
update_sequence_chooser();
|
||||||
update_tool_buttons();
|
update_tool_buttons();
|
||||||
|
|
@ -136,7 +147,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
||||||
show_time (Time::ZERO);
|
show_time (Time::ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char*
|
const char*
|
||||||
TimelinePanel::get_title()
|
TimelinePanel::get_title()
|
||||||
{
|
{
|
||||||
|
|
@ -183,6 +193,13 @@ TimelinePanel::on_ibeam_tool()
|
||||||
set_tool(timeline::IBeam);
|
set_tool(timeline::IBeam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelinePanel::on_zoom(double time_scale_ratio)
|
||||||
|
{
|
||||||
|
REQUIRE(timelineWidget);
|
||||||
|
timelineWidget->zoom_view(time_scale_ratio);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelinePanel::on_zoom_in()
|
TimelinePanel::on_zoom_in()
|
||||||
{
|
{
|
||||||
|
|
@ -236,13 +253,14 @@ TimelinePanel::on_sequence_chosen()
|
||||||
{
|
{
|
||||||
weak_ptr<Sequence> sequence_ptr =
|
weak_ptr<Sequence> sequence_ptr =
|
||||||
(*iter)[sequenceChooserColumns.sequenceColumn];
|
(*iter)[sequenceChooserColumns.sequenceColumn];
|
||||||
|
|
||||||
shared_ptr<Sequence> sequence(sequence_ptr.lock());
|
shared_ptr<Sequence> sequence(sequence_ptr.lock());
|
||||||
|
|
||||||
if(sequence)
|
if(sequence)
|
||||||
{
|
{
|
||||||
shared_ptr<timeline::TimelineState> old_state(
|
shared_ptr<timeline::TimelineState> old_state(
|
||||||
timelineWidget->get_state());
|
timelineWidget->get_state());
|
||||||
REQUIRE(old_state);
|
REQUIRE(old_state);
|
||||||
|
|
||||||
if(sequence != old_state->get_sequence())
|
if(sequence != old_state->get_sequence())
|
||||||
timelineWidget->set_state(load_state(sequence));
|
timelineWidget->set_state(load_state(sequence));
|
||||||
}
|
}
|
||||||
|
|
@ -379,6 +397,7 @@ TimelinePanel::on_frame()
|
||||||
shared_ptr<timeline::TimelineState>
|
shared_ptr<timeline::TimelineState>
|
||||||
TimelinePanel::load_state(weak_ptr<Sequence> sequence)
|
TimelinePanel::load_state(weak_ptr<Sequence> sequence)
|
||||||
{
|
{
|
||||||
|
/* state exists */
|
||||||
if(contains(timelineStates, sequence))
|
if(contains(timelineStates, sequence))
|
||||||
return timelineStates[sequence];
|
return timelineStates[sequence];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@
|
||||||
#include "gui/panels/panel.hpp"
|
#include "gui/panels/panel.hpp"
|
||||||
#include "gui/widgets/timecode-widget.hpp"
|
#include "gui/widgets/timecode-widget.hpp"
|
||||||
#include "gui/widgets/timeline-widget.hpp"
|
#include "gui/widgets/timeline-widget.hpp"
|
||||||
|
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
|
||||||
|
|
||||||
#include "lib/time/timevalue.hpp"
|
#include "lib/time/timevalue.hpp"
|
||||||
|
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
|
@ -87,6 +89,7 @@ private:
|
||||||
void on_arrow_tool();
|
void on_arrow_tool();
|
||||||
void on_ibeam_tool();
|
void on_ibeam_tool();
|
||||||
|
|
||||||
|
void on_zoom(double time_scale_ratio);
|
||||||
void on_zoom_in();
|
void on_zoom_in();
|
||||||
void on_zoom_out();
|
void on_zoom_out();
|
||||||
|
|
||||||
|
|
@ -171,7 +174,7 @@ private:
|
||||||
boost::scoped_ptr<TimelineWidget> timelineWidget;
|
boost::scoped_ptr<TimelineWidget> timelineWidget;
|
||||||
|
|
||||||
std::map< boost::weak_ptr<model::Sequence>,
|
std::map< boost::weak_ptr<model::Sequence>,
|
||||||
boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796
|
boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||||
timelineStates;
|
timelineStates;
|
||||||
|
|
||||||
// Toolbar Widgets
|
// Toolbar Widgets
|
||||||
|
|
@ -191,6 +194,7 @@ private:
|
||||||
|
|
||||||
MiniButton zoomIn;
|
MiniButton zoomIn;
|
||||||
MiniButton zoomOut;
|
MiniButton zoomOut;
|
||||||
|
gui::widgets::timeline::TimelineZoomScale zoomScale;
|
||||||
|
|
||||||
Gtk::SeparatorToolItem separator2;
|
Gtk::SeparatorToolItem separator2;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ const int TimelineWidget::TrackPadding = 1;
|
||||||
const int TimelineWidget::HeaderWidth = 150;
|
const int TimelineWidget::HeaderWidth = 150;
|
||||||
const int TimelineWidget::HeaderIndentWidth = 10;
|
const int TimelineWidget::HeaderIndentWidth = 10;
|
||||||
const double TimelineWidget::ZoomIncrement = 1.25;
|
const double TimelineWidget::ZoomIncrement = 1.25;
|
||||||
const int64_t TimelineWidget::MaxScale = 30000000;
|
const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million
|
||||||
|
|
||||||
TimelineWidget::TimelineWidget(
|
TimelineWidget::TimelineWidget(
|
||||||
boost::shared_ptr<timeline::TimelineState> source_state) :
|
boost::shared_ptr<timeline::TimelineState> source_state) :
|
||||||
|
|
@ -102,6 +102,7 @@ TimelineWidget::get_state()
|
||||||
void
|
void
|
||||||
TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
|
TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
|
||||||
{
|
{
|
||||||
|
|
||||||
state = new_state;
|
state = new_state;
|
||||||
|
|
||||||
// Clear the track tree
|
// Clear the track tree
|
||||||
|
|
@ -125,16 +126,16 @@ TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
|
||||||
update_tracks();
|
update_tracks();
|
||||||
|
|
||||||
// Send the state changed signal
|
// Send the state changed signal
|
||||||
stateChangedSignal.emit();
|
stateChangedSignal.emit (state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineWidget::zoom_view(int zoom_size)
|
TimelineWidget::zoom_view(double timescale_ratio)
|
||||||
{
|
{
|
||||||
if(state)
|
if(state)
|
||||||
{
|
{
|
||||||
const int view_width = body->get_allocation().get_width();
|
const int view_width = body->get_allocation().get_width();
|
||||||
state->get_view_window().zoom_view(view_width / 2, zoom_size);
|
state->get_view_window().zoom_view(view_width / 2, timescale_ratio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,7 +179,7 @@ TimelineWidget::hovering_track_changed_signal() const
|
||||||
return hoveringTrackChangedSignal;
|
return hoveringTrackChangedSignal;
|
||||||
}
|
}
|
||||||
|
|
||||||
sigc::signal<void>
|
TimelineWidget::TimelineStateChangeSignal
|
||||||
TimelineWidget::state_changed_signal() const
|
TimelineWidget::state_changed_signal() const
|
||||||
{
|
{
|
||||||
return stateChangedSignal;
|
return stateChangedSignal;
|
||||||
|
|
@ -212,10 +213,12 @@ TimelineWidget::on_view_window_changed()
|
||||||
if(state)
|
if(state)
|
||||||
{
|
{
|
||||||
timeline::TimelineViewWindow &window = state->get_view_window();
|
timeline::TimelineViewWindow &window = state->get_view_window();
|
||||||
|
|
||||||
const int view_width = body->get_allocation().get_width();
|
const int view_width = body->get_allocation().get_width();
|
||||||
|
|
||||||
horizontalAdjustment.set_page_size(
|
horizontalAdjustment.set_page_size(
|
||||||
window.get_time_scale() * view_width);
|
window.get_time_scale() * view_width);
|
||||||
|
|
||||||
horizontalAdjustment.set_value(_raw(window.get_time_offset()));
|
horizontalAdjustment.set_value(_raw(window.get_time_offset()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ public:
|
||||||
* @param zoom_size The number of steps to zoom by. The scale factor
|
* @param zoom_size The number of steps to zoom by. The scale factor
|
||||||
* is 1.25^(-zoom_size).
|
* is 1.25^(-zoom_size).
|
||||||
*/
|
*/
|
||||||
void zoom_view(int zoom_size);
|
void zoom_view(double timescale_ratio);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the type of the tool currently active.
|
* Gets the type of the tool currently active.
|
||||||
|
|
@ -115,14 +115,16 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* ===== Signals ===== */
|
/* ===== Signals ===== */
|
||||||
|
typedef sigc::signal<void, boost::shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal;
|
||||||
|
typedef sigc::signal<void, boost::shared_ptr<timeline::Track> > HoveringTrackChangedSignal;
|
||||||
|
|
||||||
sigc::signal<void, lib::time::Time> mouse_hover_signal() const;
|
sigc::signal<void, lib::time::Time> mouse_hover_signal() const;
|
||||||
|
|
||||||
sigc::signal<void> playback_period_drag_released_signal() const;
|
sigc::signal<void> playback_period_drag_released_signal() const;
|
||||||
|
|
||||||
sigc::signal<void, boost::shared_ptr<timeline::Track> >
|
HoveringTrackChangedSignal hovering_track_changed_signal() const;
|
||||||
hovering_track_changed_signal() const;
|
|
||||||
|
|
||||||
sigc::signal<void> state_changed_signal() const;
|
TimelineStateChangeSignal state_changed_signal() const;
|
||||||
|
|
||||||
/* ===== Events ===== */
|
/* ===== Events ===== */
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -275,9 +277,8 @@ protected:
|
||||||
// Signals
|
// Signals
|
||||||
sigc::signal<void, Time> mouseHoverSignal;
|
sigc::signal<void, Time> mouseHoverSignal;
|
||||||
sigc::signal<void> playbackPeriodDragReleasedSignal;
|
sigc::signal<void> playbackPeriodDragReleasedSignal;
|
||||||
sigc::signal<void, boost::shared_ptr<timeline::Track> >
|
HoveringTrackChangedSignal hoveringTrackChangedSignal;
|
||||||
hoveringTrackChangedSignal;
|
TimelineStateChangeSignal stateChangedSignal;
|
||||||
sigc::signal<void> stateChangedSignal;
|
|
||||||
|
|
||||||
bool update_tracks_frozen;
|
bool update_tracks_frozen;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ using namespace lumiera;
|
||||||
|
|
||||||
using gui::util::CairoUtil;
|
using gui::util::CairoUtil;
|
||||||
|
|
||||||
|
using boost::shared_ptr; ////////////////////TICKET #796
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
namespace timeline {
|
namespace timeline {
|
||||||
|
|
@ -61,7 +63,7 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget)
|
||||||
register_styles();
|
register_styles();
|
||||||
|
|
||||||
// Reset the state
|
// Reset the state
|
||||||
on_state_changed();
|
propagateStateChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineBody::~TimelineBody()
|
TimelineBody::~TimelineBody()
|
||||||
|
|
@ -69,6 +71,13 @@ TimelineBody::~TimelineBody()
|
||||||
WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here");
|
WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineViewWindow&
|
||||||
|
TimelineBody::viewWindow() const
|
||||||
|
{
|
||||||
|
REQUIRE(timelineState);
|
||||||
|
return timelineState->get_view_window();
|
||||||
|
}
|
||||||
|
|
||||||
TimelineWidget&
|
TimelineWidget&
|
||||||
TimelineBody::getTimelineWidget () const
|
TimelineBody::getTimelineWidget () const
|
||||||
{
|
{
|
||||||
|
|
@ -147,7 +156,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event)
|
||||||
// Makes sure the widget styles have been loaded
|
// Makes sure the widget styles have been loaded
|
||||||
read_styles();
|
read_styles();
|
||||||
|
|
||||||
if(timelineWidget.get_state())
|
if (timelineState)
|
||||||
{
|
{
|
||||||
// Prepare to render via cairo
|
// Prepare to render via cairo
|
||||||
const Allocation allocation = get_allocation();
|
const Allocation allocation = get_allocation();
|
||||||
|
|
@ -169,9 +178,9 @@ TimelineBody::on_scroll_event (GdkEventScroll* event)
|
||||||
{
|
{
|
||||||
REQUIRE(event != NULL);
|
REQUIRE(event != NULL);
|
||||||
|
|
||||||
if(timelineWidget.get_state())
|
if (timelineState)
|
||||||
{
|
{
|
||||||
TimelineViewWindow &window = view_window();
|
TimelineViewWindow &window = viewWindow();
|
||||||
const Allocation allocation = get_allocation();
|
const Allocation allocation = get_allocation();
|
||||||
|
|
||||||
if(event->state & GDK_CONTROL_MASK)
|
if(event->state & GDK_CONTROL_MASK)
|
||||||
|
|
@ -255,17 +264,16 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
|
||||||
{
|
{
|
||||||
REQUIRE(event != NULL);
|
REQUIRE(event != NULL);
|
||||||
|
|
||||||
if(timelineWidget.get_state())
|
if (timelineState)
|
||||||
{
|
{
|
||||||
// Handle a middle-mouse drag if one is occuring
|
// Handle a middle-mouse drag if one is occuring
|
||||||
switch(dragType)
|
switch(dragType)
|
||||||
{
|
{
|
||||||
case Shift:
|
case Shift:
|
||||||
{
|
{
|
||||||
TimelineViewWindow &window = view_window();
|
|
||||||
|
|
||||||
/////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view!
|
/////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view!
|
||||||
///////////////////////////// : either encapsulate this entirely here, or leave it to the timeline view!
|
///////////////////////////// : either encapsulate this entirely here, or leave it to the timeline view!
|
||||||
|
TimelineViewWindow &window = viewWindow();
|
||||||
const int64_t scale = window.get_time_scale();
|
const int64_t scale = window.get_time_scale();
|
||||||
window.set_time_offset(beginShiftTimeOffset
|
window.set_time_offset(beginShiftTimeOffset
|
||||||
+ TimeValue(scale * (mouseDownX - event->x)));
|
+ TimeValue(scale * (mouseDownX - event->x)));
|
||||||
|
|
@ -294,12 +302,20 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineBody::on_state_changed()
|
TimelineBody::on_state_changed (shared_ptr<TimelineState> newState)
|
||||||
{
|
{
|
||||||
if(timelineWidget.get_state())
|
REQUIRE (newState);
|
||||||
|
timelineState = newState;
|
||||||
|
propagateStateChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineBody::propagateStateChange()
|
||||||
|
{
|
||||||
|
if (timelineState)
|
||||||
{
|
{
|
||||||
// Connect up some events
|
// Connect up some events
|
||||||
view_window().changed_signal().connect(
|
viewWindow().changed_signal().connect(
|
||||||
sigc::mem_fun(this, &TimelineBody::on_update_view) );
|
sigc::mem_fun(this, &TimelineBody::on_update_view) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,7 +389,7 @@ TimelineBody::draw_track(Cairo::RefPtr<Cairo::Context> cr,
|
||||||
|
|
||||||
// Render the track
|
// Render the track
|
||||||
cr->save();
|
cr->save();
|
||||||
TimelineViewWindow &window = view_window();
|
TimelineViewWindow &window = viewWindow();
|
||||||
timeline_track->draw_track(cr, &window);
|
timeline_track->draw_track(cr, &window);
|
||||||
cr->restore();
|
cr->restore();
|
||||||
}
|
}
|
||||||
|
|
@ -386,12 +402,11 @@ TimelineBody::draw_selection(Cairo::RefPtr<Cairo::Context> cr)
|
||||||
// Prepare
|
// Prepare
|
||||||
const Allocation allocation = get_allocation();
|
const Allocation allocation = get_allocation();
|
||||||
|
|
||||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
REQUIRE(timelineState);
|
||||||
REQUIRE(state);
|
|
||||||
|
|
||||||
TimelineViewWindow const& window = state->get_view_window();
|
TimelineViewWindow const& window = timelineState->get_view_window();
|
||||||
const int start_x = window.time_to_x(state->getSelectionStart());
|
const int start_x = window.time_to_x(timelineState->getSelectionStart());
|
||||||
const int end_x = window.time_to_x(state->getSelectionEnd());
|
const int end_x = window.time_to_x(timelineState->getSelectionEnd());
|
||||||
|
|
||||||
// Draw the cover
|
// Draw the cover
|
||||||
if(end_x > 0 && start_x < allocation.get_width())
|
if(end_x > 0 && start_x < allocation.get_width())
|
||||||
|
|
@ -427,16 +442,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr)
|
||||||
{
|
{
|
||||||
REQUIRE(cr);
|
REQUIRE(cr);
|
||||||
|
|
||||||
// Prepare
|
if (timelineState)
|
||||||
|
|
||||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
|
||||||
if(state)
|
|
||||||
{
|
{
|
||||||
if (!state->isPlaying()) return;
|
if (!timelineState->isPlaying()) return;
|
||||||
|
|
||||||
const Allocation allocation = get_allocation();
|
const Allocation allocation = get_allocation();
|
||||||
Time point = state->getPlaybackPoint();
|
Time point = timelineState->getPlaybackPoint();
|
||||||
const int x = view_window().time_to_x(point);
|
const int x = viewWindow().time_to_x(point);
|
||||||
|
|
||||||
// Set source
|
// Set source
|
||||||
cr->set_source(playbackPointColour);
|
cr->set_source(playbackPointColour);
|
||||||
|
|
@ -455,10 +467,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr)
|
||||||
void
|
void
|
||||||
TimelineBody::begin_shift_drag()
|
TimelineBody::begin_shift_drag()
|
||||||
{
|
{
|
||||||
if(timelineWidget.get_state())
|
if (timelineState)
|
||||||
{
|
{
|
||||||
dragType = Shift;
|
dragType = Shift;
|
||||||
beginShiftTimeOffset = view_window().get_time_offset();
|
beginShiftTimeOffset = viewWindow().get_time_offset();
|
||||||
beginShiftVerticalOffset = get_vertical_offset();
|
beginShiftVerticalOffset = get_vertical_offset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -475,14 +487,6 @@ TimelineBody::set_vertical_offset(int offset)
|
||||||
timelineWidget.verticalAdjustment.set_value(offset);
|
timelineWidget.verticalAdjustment.set_value(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineViewWindow&
|
|
||||||
TimelineBody::view_window() const
|
|
||||||
{
|
|
||||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
|
||||||
REQUIRE(state);
|
|
||||||
return state->get_view_window();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineBody::register_styles() const
|
TimelineBody::register_styles() const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -68,10 +68,7 @@ public:
|
||||||
*/
|
*/
|
||||||
TimelineBody(gui::widgets::TimelineWidget &timeline_widget);
|
TimelineBody(gui::widgets::TimelineWidget &timeline_widget);
|
||||||
|
|
||||||
/**
|
virtual ~TimelineBody();
|
||||||
* Destructor
|
|
||||||
*/
|
|
||||||
~TimelineBody();
|
|
||||||
|
|
||||||
TimelineWidget&
|
TimelineWidget&
|
||||||
getTimelineWidget () const;
|
getTimelineWidget () const;
|
||||||
|
|
@ -125,13 +122,18 @@ protected:
|
||||||
bool on_motion_notify_event(GdkEventMotion *event);
|
bool on_motion_notify_event(GdkEventMotion *event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event handler for when the TimelineWidget's state object is
|
* The event handler for when the TimelineWidget's state is switched.
|
||||||
* replaced.
|
|
||||||
*/
|
*/
|
||||||
void on_state_changed();
|
void on_state_changed (boost::shared_ptr<TimelineState> newState);
|
||||||
|
|
||||||
/* ===== Internals ===== */
|
/* ===== Internals ===== */
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* Access the current timeline view window
|
||||||
|
* @warning must not be called unless the TimlineWidget
|
||||||
|
* has a valid state.
|
||||||
|
*/
|
||||||
|
TimelineViewWindow& viewWindow() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the timeline tracks.
|
* Draws the timeline tracks.
|
||||||
|
|
@ -161,12 +163,8 @@ private:
|
||||||
|
|
||||||
void set_vertical_offset(int offset);
|
void set_vertical_offset(int offset);
|
||||||
|
|
||||||
/**
|
/** adjust to the new timeline state */
|
||||||
* A helper function to get the view window
|
void propagateStateChange();
|
||||||
* @remarks This function must not be called unless the TimlineWidget
|
|
||||||
* has a valid state.
|
|
||||||
*/
|
|
||||||
TimelineViewWindow& view_window() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers all the styles that this class will respond to.
|
* Registers all the styles that this class will respond to.
|
||||||
|
|
@ -202,6 +200,8 @@ private:
|
||||||
Cairo::RefPtr<Cairo::SolidPattern> playbackPointColour;
|
Cairo::RefPtr<Cairo::SolidPattern> playbackPointColour;
|
||||||
|
|
||||||
gui::widgets::TimelineWidget &timelineWidget;
|
gui::widgets::TimelineWidget &timelineWidget;
|
||||||
|
boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||||
|
|
||||||
|
|
||||||
friend class Tool;
|
friend class Tool;
|
||||||
friend class ArrowTool;
|
friend class ArrowTool;
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,14 @@ TimelineRuler::TimelineRuler (TimelineWidget &timeline_widget)
|
||||||
register_styles();
|
register_styles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineViewWindow&
|
||||||
|
TimelineRuler::viewWindow() const
|
||||||
|
{
|
||||||
|
REQUIRE(timelineState);
|
||||||
|
return timelineState->get_view_window();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineRuler::set_mouse_chevron_offset(int offset)
|
TimelineRuler::set_mouse_chevron_offset(int offset)
|
||||||
{
|
{
|
||||||
|
|
@ -114,7 +122,7 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
|
||||||
if(!window)
|
if(!window)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(timelineWidget.get_state())
|
if (timelineState)
|
||||||
{
|
{
|
||||||
// Prepare to render via cairo
|
// Prepare to render via cairo
|
||||||
const Allocation allocation = get_allocation();
|
const Allocation allocation = get_allocation();
|
||||||
|
|
@ -160,11 +168,11 @@ TimelineRuler::on_button_press_event(GdkEventButton* event)
|
||||||
{
|
{
|
||||||
REQUIRE(event != NULL);
|
REQUIRE(event != NULL);
|
||||||
|
|
||||||
if(timelineWidget.get_state())
|
if (timelineState)
|
||||||
{
|
{
|
||||||
if(event->button == 1)
|
if(event->button == 1)
|
||||||
{
|
{
|
||||||
pinnedDragTime = view_window().x_to_time(event->x);
|
pinnedDragTime = viewWindow().x_to_time(event->x);
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -219,34 +227,40 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineRuler::on_state_changed()
|
TimelineRuler::on_state_changed (shared_ptr<TimelineState> newState)
|
||||||
{
|
{
|
||||||
if(timelineWidget.get_state())
|
REQUIRE (newState);
|
||||||
|
timelineState = newState;
|
||||||
|
|
||||||
|
propagateStateChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineRuler::propagateStateChange()
|
||||||
{
|
{
|
||||||
// Connect up some events
|
// Connect up some events
|
||||||
view_window().changed_signal().connect(
|
viewWindow().changed_signal().connect(
|
||||||
sigc::mem_fun(this, &TimelineRuler::on_update_view) );
|
sigc::mem_fun(this, &TimelineRuler::on_update_view) );
|
||||||
}
|
|
||||||
|
|
||||||
// Redraw
|
// Redraw
|
||||||
on_update_view();
|
on_update_view();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineRuler::set_leading_x(const int x)
|
TimelineRuler::set_leading_x(const int x)
|
||||||
{
|
{
|
||||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
if (timelineState)
|
||||||
|
|
||||||
if(state)
|
|
||||||
{
|
{
|
||||||
TimeVar newStartPoint (view_window().x_to_time(x));
|
TimeVar newStartPoint (viewWindow().x_to_time(x));
|
||||||
Offset selectionLength (pinnedDragTime, newStartPoint);
|
Offset selectionLength (pinnedDragTime, newStartPoint);
|
||||||
|
|
||||||
if (newStartPoint > pinnedDragTime)
|
if (newStartPoint > pinnedDragTime)
|
||||||
newStartPoint=pinnedDragTime; // use the smaller one as selection start
|
newStartPoint=pinnedDragTime; // use the smaller one as selection start
|
||||||
|
|
||||||
state->setPlaybackPeriod (Mutation::changeTime(newStartPoint) );
|
timelineState->setPlaybackPeriod (Mutation::changeTime(newStartPoint) );
|
||||||
state->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
|
timelineState->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
|
||||||
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once
|
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once
|
||||||
////////////////////TODO : code duplication with timeline-ibeam-tool 205
|
////////////////////TODO : code duplication with timeline-ibeam-tool 205
|
||||||
}
|
}
|
||||||
|
|
@ -260,7 +274,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cr,
|
||||||
REQUIRE(ruler_rect.get_width() > 0);
|
REQUIRE(ruler_rect.get_width() > 0);
|
||||||
REQUIRE(ruler_rect.get_height() > 0);
|
REQUIRE(ruler_rect.get_height() > 0);
|
||||||
|
|
||||||
const TimelineViewWindow &window = view_window();
|
const TimelineViewWindow &window = viewWindow();
|
||||||
const gavl_time_t left_offset = _raw(window.get_time_offset());
|
const gavl_time_t left_offset = _raw(window.get_time_offset());
|
||||||
const int64_t time_scale = window.get_time_scale();
|
const int64_t time_scale = window.get_time_scale();
|
||||||
|
|
||||||
|
|
@ -361,16 +375,15 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr,
|
||||||
REQUIRE(cr);
|
REQUIRE(cr);
|
||||||
REQUIRE(ruler_rect.get_width() > 0);
|
REQUIRE(ruler_rect.get_width() > 0);
|
||||||
REQUIRE(ruler_rect.get_height() > 0);
|
REQUIRE(ruler_rect.get_height() > 0);
|
||||||
|
REQUIRE(timelineState);
|
||||||
|
|
||||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
const TimelineViewWindow &window = timelineState->get_view_window();
|
||||||
REQUIRE(state);
|
|
||||||
const TimelineViewWindow &window = state->get_view_window();
|
|
||||||
|
|
||||||
Glib::RefPtr<Style> style = get_style();
|
Glib::RefPtr<Style> style = get_style();
|
||||||
Gdk::Cairo::set_source_color(cr, style->get_fg(STATE_NORMAL));
|
Gdk::Cairo::set_source_color(cr, style->get_fg(STATE_NORMAL));
|
||||||
|
|
||||||
// Draw the selection start chevron
|
// Draw the selection start chevron
|
||||||
const int a = 1 + window.time_to_x(state->getSelectionStart());
|
const int a = 1 + window.time_to_x(timelineState->getSelectionStart());
|
||||||
if(a >= 0 && a < ruler_rect.get_width())
|
if(a >= 0 && a < ruler_rect.get_width())
|
||||||
{
|
{
|
||||||
cr->move_to(a, ruler_rect.get_height());
|
cr->move_to(a, ruler_rect.get_height());
|
||||||
|
|
@ -380,7 +393,7 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the selection end chevron
|
// Draw the selection end chevron
|
||||||
const int b = window.time_to_x(state->getSelectionEnd());
|
const int b = window.time_to_x(timelineState->getSelectionEnd());
|
||||||
if(b >= 0 && b < ruler_rect.get_width())
|
if(b >= 0 && b < ruler_rect.get_width())
|
||||||
{
|
{
|
||||||
cr->move_to(b, ruler_rect.get_height());
|
cr->move_to(b, ruler_rect.get_height());
|
||||||
|
|
@ -397,18 +410,17 @@ TimelineRuler::draw_playback_period(Cairo::RefPtr<Cairo::Context> cr,
|
||||||
REQUIRE(cr);
|
REQUIRE(cr);
|
||||||
REQUIRE(ruler_rect.get_width() > 0);
|
REQUIRE(ruler_rect.get_width() > 0);
|
||||||
REQUIRE(ruler_rect.get_height() > 0);
|
REQUIRE(ruler_rect.get_height() > 0);
|
||||||
|
REQUIRE(timelineState);
|
||||||
|
|
||||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
const TimelineViewWindow &window = timelineState->get_view_window();
|
||||||
REQUIRE(state);
|
|
||||||
const TimelineViewWindow &window = state->get_view_window();
|
|
||||||
|
|
||||||
// Calculate coordinates
|
// Calculate coordinates
|
||||||
const float halfSize = playbackPeriodArrowSize / 2;
|
const float halfSize = playbackPeriodArrowSize / 2;
|
||||||
|
|
||||||
const float a = 1.5f + window.time_to_x(state->getPlaybackPeriodStart());
|
const float a = 1.5f + window.time_to_x(timelineState->getPlaybackPeriodStart());
|
||||||
|
|
||||||
const float b = a + halfSize;
|
const float b = a + halfSize;
|
||||||
const float d = 0.5f + window.time_to_x(state->getPlaybackPeriodEnd());
|
const float d = 0.5f + window.time_to_x(timelineState->getPlaybackPeriodEnd());
|
||||||
|
|
||||||
const float c = d - halfSize;
|
const float c = d - halfSize;
|
||||||
|
|
||||||
|
|
@ -465,14 +477,12 @@ TimelineRuler::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr,
|
||||||
REQUIRE(cr);
|
REQUIRE(cr);
|
||||||
REQUIRE(ruler_rect.get_width() > 0);
|
REQUIRE(ruler_rect.get_width() > 0);
|
||||||
REQUIRE(ruler_rect.get_height() > 0);
|
REQUIRE(ruler_rect.get_height() > 0);
|
||||||
|
REQUIRE(timelineState);
|
||||||
|
|
||||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
if (!timelineState->isPlaying()) return;
|
||||||
REQUIRE(state);
|
|
||||||
if (!state->isPlaying()) return;
|
|
||||||
|
|
||||||
TimelineViewWindow const& window = state->get_view_window();
|
Time point = timelineState->getPlaybackPoint();
|
||||||
Time point = state->getPlaybackPoint();
|
const int x = viewWindow().time_to_x(point);
|
||||||
const int x = window.time_to_x(point);
|
|
||||||
|
|
||||||
cr->move_to(x + 0.5, ruler_rect.get_height());
|
cr->move_to(x + 0.5, ruler_rect.get_height());
|
||||||
cr->rel_line_to(0, -playbackPointSize);
|
cr->rel_line_to(0, -playbackPointSize);
|
||||||
|
|
@ -494,7 +504,6 @@ TimelineRuler::calculate_major_spacing() const
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
const int64_t time_scale = view_window().get_time_scale();
|
|
||||||
const gavl_time_t major_spacings[] = {
|
const gavl_time_t major_spacings[] = {
|
||||||
GAVL_TIME_SCALE / 1000,
|
GAVL_TIME_SCALE / 1000,
|
||||||
GAVL_TIME_SCALE / 400,
|
GAVL_TIME_SCALE / 400,
|
||||||
|
|
@ -520,6 +529,8 @@ TimelineRuler::calculate_major_spacing() const
|
||||||
60ll * 60ll * GAVL_TIME_SCALE
|
60ll * 60ll * GAVL_TIME_SCALE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int64_t time_scale = viewWindow().get_time_scale();
|
||||||
|
|
||||||
for(i = 0; i < sizeof(major_spacings) / sizeof(gavl_time_t); i++)
|
for(i = 0; i < sizeof(major_spacings) / sizeof(gavl_time_t); i++)
|
||||||
{
|
{
|
||||||
const int64_t division_width = major_spacings[i] / time_scale;
|
const int64_t division_width = major_spacings[i] / time_scale;
|
||||||
|
|
@ -531,14 +542,6 @@ TimelineRuler::calculate_major_spacing() const
|
||||||
return major_spacings[i];
|
return major_spacings[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineViewWindow&
|
|
||||||
TimelineRuler::view_window() const
|
|
||||||
{
|
|
||||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
|
||||||
REQUIRE(state);
|
|
||||||
return state->get_view_window();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineRuler::register_styles() const
|
TimelineRuler::register_styles() const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "gui/gtk-lumiera.hpp"
|
#include "gui/gtk-lumiera.hpp"
|
||||||
#include "lib/time/timevalue.hpp"
|
#include "lib/time/timevalue.hpp"
|
||||||
|
#include "gui/widgets/timeline/timeline-state.hpp"
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
@ -108,10 +109,9 @@ private:
|
||||||
void on_size_allocate(Gtk::Allocation& allocation);
|
void on_size_allocate(Gtk::Allocation& allocation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event handler for when the TimelineWidget's state object is
|
* The event handler for when the TimelineWidget's state is switched.
|
||||||
* replaced.
|
|
||||||
*/
|
*/
|
||||||
void on_state_changed();
|
void on_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* ===== Internal Methods ===== */
|
/* ===== Internal Methods ===== */
|
||||||
|
|
@ -162,6 +162,12 @@ private:
|
||||||
void draw_playback_point(Cairo::RefPtr<Cairo::Context> cr,
|
void draw_playback_point(Cairo::RefPtr<Cairo::Context> cr,
|
||||||
const Gdk::Rectangle ruler_rect);
|
const Gdk::Rectangle ruler_rect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After notification of a timeline state switch
|
||||||
|
* do any local adjustments to adapt to the new state
|
||||||
|
*/
|
||||||
|
void propagateStateChange();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the current zoom, this function calculates the preiod
|
* Given the current zoom, this function calculates the preiod
|
||||||
* between major graduations on the ruler scale.
|
* between major graduations on the ruler scale.
|
||||||
|
|
@ -170,9 +176,9 @@ private:
|
||||||
gavl_time_t calculate_major_spacing() const;
|
gavl_time_t calculate_major_spacing() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper function to get the view window
|
* Access current timeline view window
|
||||||
*/
|
*/
|
||||||
TimelineViewWindow& view_window() const;
|
TimelineViewWindow& viewWindow() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers all the styles that this class will respond to.
|
* Registers all the styles that this class will respond to.
|
||||||
|
|
@ -233,6 +239,11 @@ private:
|
||||||
*/
|
*/
|
||||||
gui::widgets::TimelineWidget &timelineWidget;
|
gui::widgets::TimelineWidget &timelineWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the currently active timeline state object
|
||||||
|
*/
|
||||||
|
boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The caches image of the ruler, over which the chevrons etc. will
|
* The caches image of the ruler, over which the chevrons etc. will
|
||||||
* be drawn.
|
* be drawn.
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,9 @@ TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence
|
||||||
REQUIRE(sequence);
|
REQUIRE(sequence);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state
|
////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state
|
||||||
|
const int64_t DEFAULT_TIMELINE_SCALE =21000000;
|
||||||
|
|
||||||
viewWindow.set_time_scale(GAVL_TIME_SCALE / 200);
|
viewWindow.set_time_scale(DEFAULT_TIMELINE_SCALE);
|
||||||
|
|
||||||
setSelection (Mutation::changeTime (Time(FSecs(2))));
|
setSelection (Mutation::changeTime (Time(FSecs(2))));
|
||||||
setSelection (Mutation::changeDuration(Duration(FSecs(2))));
|
setSelection (Mutation::changeDuration(Duration(FSecs(2))));
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,6 @@ private:
|
||||||
|
|
||||||
bool isPlayback_;
|
bool isPlayback_;
|
||||||
|
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -31,29 +31,29 @@ namespace gui {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
namespace timeline {
|
namespace timeline {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale)
|
TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale)
|
||||||
: timeOffset(offset)
|
: timeOffset(offset)
|
||||||
, timeScale(scale)
|
, timeScale(scale)
|
||||||
{
|
{
|
||||||
|
TODO("Create a function to limit timescale between 1 and MaxScale");
|
||||||
|
TODO("TICKET #795 Some functions need to be private");
|
||||||
}
|
}
|
||||||
|
|
||||||
Offset
|
Offset
|
||||||
TimelineViewWindow::get_time_offset() const /////////////////////TODO: this function shouldn't be accessible from outside
|
TimelineViewWindow::get_time_offset() const /////////////////////TICKET #795: this function shouldn't be accessible from outside
|
||||||
{
|
{
|
||||||
return Offset (timeOffset);
|
return Offset (timeOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TODO: this function shouldn't be accessible from outside
|
TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TICKET #795: this function shouldn't be accessible from outside
|
||||||
{
|
{
|
||||||
timeOffset = offset;
|
timeOffset = offset;
|
||||||
changedSignal.emit();
|
changedSignal.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
TimelineViewWindow::get_time_scale() const /////////////////////TODO: this function shouldn't be accessible from outside
|
TimelineViewWindow::get_time_scale() const /////////////////////TICKET #795: this function shouldn't be accessible from outside
|
||||||
{
|
{
|
||||||
return timeScale;
|
return timeScale;
|
||||||
}
|
}
|
||||||
|
|
@ -66,18 +66,36 @@ TimelineViewWindow::set_time_scale(int64_t scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineViewWindow::zoom_view(int point, int zoom_size)
|
TimelineViewWindow::set_time_scale(double ratio)
|
||||||
{
|
{
|
||||||
int64_t new_time_scale = (double)timeScale * pow(1.25, -zoom_size);
|
int64_t max = TimelineWidget::MaxScale;
|
||||||
|
int64_t min = 1;
|
||||||
|
|
||||||
// Limit zooming in too close
|
if(ratio <= 0.0)
|
||||||
if(new_time_scale < 1) new_time_scale = 1;
|
{
|
||||||
|
set_time_scale((int64_t)min);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Nudge zoom problems caused by integer rounding
|
if(ratio > 1.0)
|
||||||
if(new_time_scale == timeScale && zoom_size < 0)
|
{
|
||||||
new_time_scale++;
|
ratio = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_time_scale((int64_t)(ratio * max));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineViewWindow::zoom_view(int point, double time_scale_ratio)
|
||||||
|
{
|
||||||
|
TODO("Find a Central place for a Zoom Smoothing Factor Variable. Right now it is hard coded at 9.0");
|
||||||
|
int64_t new_time_scale =
|
||||||
|
(int64_t)( pow(time_scale_ratio, 9.0) * (double)TimelineWidget::MaxScale);
|
||||||
|
|
||||||
|
/* Prevent Zooming in To Close and Far */
|
||||||
|
if(new_time_scale < 1)
|
||||||
|
new_time_scale = 1;
|
||||||
|
|
||||||
// Limit zooming out too far
|
|
||||||
if(new_time_scale > TimelineWidget::MaxScale)
|
if(new_time_scale > TimelineWidget::MaxScale)
|
||||||
new_time_scale = TimelineWidget::MaxScale;
|
new_time_scale = TimelineWidget::MaxScale;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,14 +94,14 @@ public:
|
||||||
* zero
|
* zero
|
||||||
*/
|
*/
|
||||||
void set_time_scale(int64_t time_scale);
|
void set_time_scale(int64_t time_scale);
|
||||||
|
void set_time_scale(double ratio);
|
||||||
/**
|
/**
|
||||||
* Zooms the view in or out as by a number of steps while keeping a
|
* Zooms the view in or out as by a number of steps while keeping a
|
||||||
* given point on the timeline still.
|
* given point on the timeline still.
|
||||||
* @param zoom_size The number of steps to zoom by. The scale factor
|
* @param new_time_scale The number of steps to zoom by. The scale factor
|
||||||
* is 1.25^(-zoom_size).
|
* is 1.25^(-new_time_scale).
|
||||||
*/
|
*/
|
||||||
void zoom_view(int point, int zoom_size);
|
void zoom_view(int point, double timescale_ratio);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrolls the view horizontally as a proportion of the view area.
|
* Scrolls the view horizontally as a proportion of the view area.
|
||||||
|
|
|
||||||
155
src/gui/widgets/timeline/timeline-zoom-scale.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
timeline-zoom-scale.cpp - Implementation of the zoom scale widget
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Michael R. Fisher <mfisher31@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
* *****************************************************/
|
||||||
|
|
||||||
|
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
|
||||||
|
#include "gui/widgets/timeline-widget.hpp"
|
||||||
|
|
||||||
|
using namespace Gtk;
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
class TimelineWidget;
|
||||||
|
|
||||||
|
namespace timeline {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo The initial adjustment value needs to
|
||||||
|
* match what the TimelineViewWindow's actual timeScale
|
||||||
|
* Value is. TimelineViewWindow::get_time_scale() is
|
||||||
|
* currently a public method, but will soon be private.
|
||||||
|
* Maybe TimelineViewWindow can have a zoom_adjustment
|
||||||
|
* that gets passed to this widget's Constructor?
|
||||||
|
*
|
||||||
|
* @todo actually there is a more involved problem.
|
||||||
|
* The TimelineWidget maintains a TimelineState, which in turn
|
||||||
|
* owns the TimelineViewWindow. Now, the problem is: when we
|
||||||
|
* switch to another Sequence (View), then this TimelineState
|
||||||
|
* gets switched too, causing also a entirely different TimelineViewWindow
|
||||||
|
* to become effective. Thus
|
||||||
|
* - how can we managed to be notified from that switch?
|
||||||
|
* - problem is: TimelineZoomScale widget is owned by the TimelinePannel.
|
||||||
|
* Likewise, TimelineWidget is owned by the TimelinePannel. But the
|
||||||
|
* state handling/switching logic is embedded within TimelineWidget
|
||||||
|
* - and finally: how can we translate the actual scale (in time units),
|
||||||
|
* as maintained within TimelineViewWindow, back into the adjustment
|
||||||
|
* used here (which uses a relative scale 0...1.0 )
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
TimelineZoomScale::TimelineZoomScale()
|
||||||
|
: HBox()
|
||||||
|
, adjustment(0.5, 0.0, 1.0, 0.000001)
|
||||||
|
, slider()
|
||||||
|
, zoomIn(Stock::ZOOM_IN)
|
||||||
|
, zoomOut(Stock::ZOOM_OUT)
|
||||||
|
, button_step_size(0.03)
|
||||||
|
{
|
||||||
|
/* Setup the Slider Control */
|
||||||
|
slider.set_adjustment(adjustment);
|
||||||
|
slider.set_size_request(123,10);
|
||||||
|
slider.set_digits(6);
|
||||||
|
slider.set_inverted(true);
|
||||||
|
slider.set_draw_value(false);
|
||||||
|
|
||||||
|
/* Make our connections */
|
||||||
|
zoomIn.signal_clicked().
|
||||||
|
connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_in_clicked));
|
||||||
|
|
||||||
|
zoomOut.signal_clicked().
|
||||||
|
connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked));
|
||||||
|
|
||||||
|
adjustment.signal_value_changed().
|
||||||
|
connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom));
|
||||||
|
|
||||||
|
/* Add Our Widgets and show them */
|
||||||
|
pack_start(zoomOut,PACK_SHRINK);
|
||||||
|
pack_start(slider,PACK_SHRINK);
|
||||||
|
pack_start(zoomIn,PACK_SHRINK);
|
||||||
|
|
||||||
|
show_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineZoomScale::wireTimelineState (boost::shared_ptr<TimelineState> currentState,
|
||||||
|
TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
|
||||||
|
{
|
||||||
|
on_timeline_state_changed (currentState);
|
||||||
|
stateChangeSignal.connect (sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr<TimelineState> newState)
|
||||||
|
{
|
||||||
|
REQUIRE (newState);
|
||||||
|
timelineState = newState;
|
||||||
|
|
||||||
|
int64_t current_scale =
|
||||||
|
getViewWindow().get_time_scale();
|
||||||
|
|
||||||
|
double linear_scale =
|
||||||
|
(double) current_scale / (double) TimelineWidget::MaxScale;
|
||||||
|
|
||||||
|
/* We have to Revese the Smoothing */
|
||||||
|
TODO("Find a central place for ZoomSmoothingFactor Variable. right now it is 9.0");
|
||||||
|
double new_relative_scale =
|
||||||
|
pow(linear_scale,(1.0/9.0));
|
||||||
|
|
||||||
|
adjustment.set_value(new_relative_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineZoomScale::on_zoom_in_clicked()
|
||||||
|
{
|
||||||
|
double newValue = adjustment.get_value() - button_step_size;
|
||||||
|
adjustment.set_value(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineZoomScale::on_zoom_out_clicked()
|
||||||
|
{
|
||||||
|
double newValue = adjustment.get_value() + button_step_size;
|
||||||
|
adjustment.set_value(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineZoomScale::on_zoom()
|
||||||
|
{
|
||||||
|
zoomSignal.emit(adjustment.get_value()) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigc::signal<void, double>
|
||||||
|
TimelineZoomScale::signal_zoom()
|
||||||
|
{
|
||||||
|
return zoomSignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimelineViewWindow&
|
||||||
|
TimelineZoomScale::getViewWindow()
|
||||||
|
{
|
||||||
|
REQUIRE (timelineState, "lifecycle error");
|
||||||
|
return timelineState->get_view_window();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace widgets
|
||||||
|
} // namespace timeline
|
||||||
110
src/gui/widgets/timeline/timeline-zoom-scale.hpp
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
timeline-zoom-scale.hpp - Declaration of the zoom scale widget
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Michael R. Fisher <mfisher31@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
/** @file timeline-zoom-scale.hpp
|
||||||
|
** This file contains the definition of the zoom scale widget
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TIMELINE_ZOOM_SCALE_HPP
|
||||||
|
#define TIMELINE_ZOOM_SCALE_HPP
|
||||||
|
|
||||||
|
#include "gui/gtk-lumiera.hpp"
|
||||||
|
#include "gui/widgets/mini-button.hpp"
|
||||||
|
#include "gui/widgets/timeline-widget.hpp"
|
||||||
|
#include "gui/widgets/timeline/timeline-state.hpp"
|
||||||
|
#include "gui/widgets/timeline/timeline-view-window.hpp"
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
using namespace Gtk;
|
||||||
|
using namespace gui::widgets;
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
namespace widgets {
|
||||||
|
namespace timeline {
|
||||||
|
|
||||||
|
class TimelineZoomScale : public Gtk::HBox
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
TimelineZoomScale();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessor method to the zoomSignal
|
||||||
|
* @return the zoomSignal
|
||||||
|
*/
|
||||||
|
sigc::signal<void, double> signal_zoom();
|
||||||
|
|
||||||
|
void wireTimelineState (boost::shared_ptr<TimelineState> currentState,
|
||||||
|
TimelineWidget::TimelineStateChangeSignal);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Event Handlers */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the slider position when the timeline state
|
||||||
|
* is changed.
|
||||||
|
*/
|
||||||
|
void on_timeline_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler for when the zoomIn Button
|
||||||
|
* is clicked
|
||||||
|
*/
|
||||||
|
void on_zoom_in_clicked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler for when the zoomIn Button
|
||||||
|
* is clicked
|
||||||
|
*/
|
||||||
|
void on_zoom_out_clicked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler for when the adjustment
|
||||||
|
* value is changed
|
||||||
|
*/
|
||||||
|
void on_zoom();
|
||||||
|
|
||||||
|
/** access current timeline state */
|
||||||
|
TimelineViewWindow& getViewWindow();
|
||||||
|
|
||||||
|
/* Widgets */
|
||||||
|
Gtk::Adjustment adjustment;
|
||||||
|
Gtk::HScale slider;
|
||||||
|
MiniButton zoomIn;
|
||||||
|
MiniButton zoomOut;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Signals */
|
||||||
|
sigc::signal<void, double> zoomSignal;
|
||||||
|
|
||||||
|
const double button_step_size;
|
||||||
|
|
||||||
|
boost::shared_ptr<TimelineState> timelineState;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace widgets
|
||||||
|
} // namespace timeline
|
||||||
|
|
||||||
|
#endif /* TIMELINE_ZOOM_SCALE_HPP */
|
||||||
|
|
@ -21,12 +21,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file display-facade.h
|
/** @file display-facade.h
|
||||||
** Major public Interface of the Lumiera GUI. While, generally speaking, the GUI
|
** Experimental Interface, allowing the Dummy-Player to access the
|
||||||
** controls the application and thus acts on its own, it exposes some services
|
** video display widget in the GUI. While, generally speaking, the GUI
|
||||||
** to the lower layers. Especially the lumiera::Display interface serves to
|
** controls the application and thus acts on its own, it might expose some
|
||||||
** hand over calculated frames to the GUI for displaying them in a viewer.
|
** services to the lower layers.
|
||||||
** It's a first draft as of 1/2009, probably it can be factored out into
|
**
|
||||||
** a more general display service in future.
|
** In the Dummy-Player design study, the lumiera::Display interface serves
|
||||||
|
** to hand over calculated frames to the GUI for displaying them in a viewer.
|
||||||
|
**
|
||||||
|
** This is a first draft as of 1/2009, and likely to be superseded by a
|
||||||
|
** better design, where rather the \em provider of an output facility
|
||||||
|
** registers with the OutputManager in the core.
|
||||||
**
|
**
|
||||||
** @see gui::GuiFacade
|
** @see gui::GuiFacade
|
||||||
** @see dummy-player-facade.h
|
** @see dummy-player-facade.h
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@
|
||||||
#include "include/interfaceproxy.hpp"
|
#include "include/interfaceproxy.hpp"
|
||||||
#include "lib/handle.hpp"
|
#include "lib/handle.hpp"
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
** Typically there is another subclass of the Facade interfaces sitting "on the other side"
|
** Typically there is another subclass of the Facade interfaces sitting "on the other side"
|
||||||
** of the interface barrier and actually implementing the functionality. The template
|
** of the interface barrier and actually implementing the functionality. The template
|
||||||
** facade::Accessor can be thought of as a factory creating such a proxy instance of the
|
** facade::Accessor can be thought of as a factory creating such a proxy instance of the
|
||||||
** facade interface for the client code to use. Typically, in instance of the \em factory
|
** facade interface for the client code to use. Typically, an instance of the \em factory
|
||||||
** is embedded (as a static functor member object) right within the otherwise abstract
|
** is embedded (as a static functor member object) right within the otherwise abstract
|
||||||
** facade interface, this way allowing the client code to write e.g. \c XYZInterface::facade()
|
** facade interface, this way allowing the client code to write e.g. \c XYZInterface::facade()
|
||||||
** to yield a reference to a proxy object implementing \c XYZInterface.
|
** to yield a reference to a proxy object implementing \c XYZInterface.
|
||||||
|
|
@ -48,16 +48,17 @@
|
||||||
** Any sort of dependency management is outside the scope of the InstanceHandle (for the core
|
** Any sort of dependency management is outside the scope of the InstanceHandle (for the core
|
||||||
** services, it is handled by the dependency of subsystems, while the plugin loader cares
|
** services, it is handled by the dependency of subsystems, while the plugin loader cares
|
||||||
** for dependency issues regarding loadable modules, thereby building on the deployment
|
** for dependency issues regarding loadable modules, thereby building on the deployment
|
||||||
** descriptors.
|
** descriptors.)
|
||||||
**
|
**
|
||||||
** For the Layer separation interfaces, the process of loading and opening is abstracted as
|
** For the Layer separation interfaces, the process of loading and opening is abstracted as
|
||||||
** an InstanceHandle object. When creating such an InstanceHandle using the appropriate
|
** an InstanceHandle object. When creating such an InstanceHandle using the appropriate
|
||||||
** template and ctor parameters, in addition to the registration with the Interface/Plugin
|
** template and ctor parameters, in addition to the registration with the Interface/Plugin
|
||||||
** system, the corresponding facade::Proxy factory is addressed and "opened" by creating
|
** system, the corresponding facade::Proxy factory is addressed and the interface instance
|
||||||
** the right proxy object instance. Similarly, when the InstanceHandle object goes out
|
** is "opened" by creating the appropriate proxy object instance. Similarly, when the
|
||||||
** of scope, prior to detaching from the Interface/Proxy system, the corresponding
|
** InstanceHandle object goes out of scope, prior to detaching from the Interface/Proxy
|
||||||
** lumiera::facade::Accessor factory is "closed", which additionally means destroying
|
** system, the corresponding lumiera::facade::Accessor frontend is "closed", which
|
||||||
** the proxy object instance and switching any further access to throwing and exception.
|
** additionally means destroying the proxy object instance and switching any
|
||||||
|
** further access to throwing and exception.
|
||||||
**
|
**
|
||||||
** While client code just includes the interface header (including interfaceproxy.hpp
|
** While client code just includes the interface header (including interfaceproxy.hpp
|
||||||
** in turn), there needs to be an actual implementation of each proxy object located in
|
** in turn), there needs to be an actual implementation of each proxy object located in
|
||||||
|
|
@ -89,7 +90,29 @@ namespace lumiera {
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
|
* access-frontend to the implementation of a service.
|
||||||
|
* Usually, an instance of Accessor is placed as static member
|
||||||
|
* right into the facade interface used to access the service.
|
||||||
|
* This allows clients to get the current actual implementation
|
||||||
|
* of that service, just by invoking the function operator on
|
||||||
|
* that member, e.g. \c lumiera::Play::facade()
|
||||||
*
|
*
|
||||||
|
* The reason for this rather indirect access technique is Lifecycle:
|
||||||
|
* Service implementations may come up and go down; moreover, a service
|
||||||
|
* might be implemented through a plugin component and thus the actual
|
||||||
|
* invocation needs to be passed through a binding layer. In this case,
|
||||||
|
* clients rather access a proxy object, which then passes on any call
|
||||||
|
* through that binding layer to the actual implementation located
|
||||||
|
* "somewhere".
|
||||||
|
*
|
||||||
|
* @note the pointer to the actual implementation is a static variable.
|
||||||
|
* This has two consequences. For one, we're dealing with kind of
|
||||||
|
* singleton service here. And, secondly, the implementation or
|
||||||
|
* proxy accessor can inherit from Accessor<FA> where FA is the
|
||||||
|
* facade interface. Being a subclass, allows the implementation
|
||||||
|
* to set that pointer when the service comes up, and to clear
|
||||||
|
* it when the service goes down and the access needs to
|
||||||
|
* be closed.
|
||||||
*/
|
*/
|
||||||
template<class FA>
|
template<class FA>
|
||||||
class Accessor
|
class Accessor
|
||||||
|
|
@ -105,7 +128,8 @@ namespace lumiera {
|
||||||
if (implProxy_)
|
if (implProxy_)
|
||||||
return *implProxy_;
|
return *implProxy_;
|
||||||
else
|
else
|
||||||
throw error::State("Facade interface currently closed.");
|
throw error::State("Facade interface currently closed."
|
||||||
|
, LUMIERA_ERROR_FACADE_LIFECYCLE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -119,8 +143,6 @@ namespace lumiera {
|
||||||
class Proxy;
|
class Proxy;
|
||||||
|
|
||||||
|
|
||||||
} // namespace facade
|
}} // namespace lumiera::facade
|
||||||
|
|
||||||
} // namespace lumiera
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
163
src/include/play-facade.h
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
PLAYER-FACADE.h - access point to the Lumiera player subsystem
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PROC_INTERFACE_PLAY_H
|
||||||
|
#define PROC_INTERFACE_PLAY_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus /* ============== C++ Interface ================= */
|
||||||
|
|
||||||
|
//#include "include/interfaceproxy.hpp"
|
||||||
|
#include "lib/handle.hpp"
|
||||||
|
#include "lib/iter-source.hpp"
|
||||||
|
#include "lib/time/control.hpp"
|
||||||
|
#include "lib/time/timevalue.hpp"
|
||||||
|
#include "include/interfaceproxy.hpp"
|
||||||
|
#include "proc/mobject/model-port.hpp"
|
||||||
|
#include "proc/mobject/output-designation.hpp"
|
||||||
|
#include "proc/mobject/session/clip.hpp"
|
||||||
|
#include "proc/mobject/session/track.hpp"
|
||||||
|
#include "proc/play/output-manager.hpp"
|
||||||
|
#include "proc/asset/timeline.hpp"
|
||||||
|
#include "proc/asset/viewer.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace proc {
|
||||||
|
namespace play {
|
||||||
|
|
||||||
|
class PlayProcess;
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
|
namespace lumiera {
|
||||||
|
|
||||||
|
namespace time = lib::time;
|
||||||
|
|
||||||
|
using std::tr1::weak_ptr;
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************
|
||||||
|
* Interface to the Player subsystem of Lumiera (Proc-Layer).
|
||||||
|
* Global access point for starting playback and render processes,
|
||||||
|
* calculating media data by running the render engine.
|
||||||
|
*
|
||||||
|
* @todo WIP-WIP-WIP 6/2011
|
||||||
|
* @note Lumiera is not yet able actually to deliver rendered data.
|
||||||
|
* @todo there should be an accompanying CL Interface defined for
|
||||||
|
* the Lumiera interface system, so the player can be
|
||||||
|
* accessed from external clients. This was left out
|
||||||
|
* for now, as we don't have either plugins, nor
|
||||||
|
* any script running capabilities yet. (5/2011)
|
||||||
|
*/
|
||||||
|
class Play
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** get an implementation instance of this service */
|
||||||
|
static lumiera::facade::Accessor<Play> facade;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Continuous playback process, which has been hooked up
|
||||||
|
* and started with a fixed set of output slots. started with a specific
|
||||||
|
* output size, format and framerate. It is a handle to a calculation process,
|
||||||
|
* which is about to produce a stream of frames and push them to the outputs.
|
||||||
|
*
|
||||||
|
* The Lifecycle of the referred playback process is managed automatically
|
||||||
|
* through this handle (by ref count). Client code is supposed to use the
|
||||||
|
* API on this handle to navigate and control the playback mode.
|
||||||
|
*
|
||||||
|
* @see handle.hpp
|
||||||
|
* @see player-service.cpp implementation
|
||||||
|
*/
|
||||||
|
class Controller
|
||||||
|
: public lib::Handle<proc::play::PlayProcess>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void play(bool); ///< play/pause toggle
|
||||||
|
void scrub(bool); ///< scrubbing playback
|
||||||
|
void adjustSpeed(double); ///< playback speed control
|
||||||
|
void go(lib::time::Time); ///< skip to the given point in time
|
||||||
|
|
||||||
|
void controlPlayhead (time::Control<time::Time> & ctrl);
|
||||||
|
void controlDuration (time::Control<time::Duration> & ctrl);
|
||||||
|
void controlLooping (time::Control<time::TimeSpan> & ctrl);
|
||||||
|
|
||||||
|
void useProxyMedia (bool);
|
||||||
|
void setQuality (uint);
|
||||||
|
|
||||||
|
bool is_playing() const;
|
||||||
|
bool is_scrubbing() const;
|
||||||
|
double getSpeed() const;
|
||||||
|
uint getQuality() const;
|
||||||
|
bool usesProxy() const;
|
||||||
|
|
||||||
|
operator weak_ptr<proc::play::PlayProcess>() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef lib::IterSource<mobject::ModelPort>::iterator ModelPorts;
|
||||||
|
typedef lib::IterSource<mobject::OutputDesignation>::iterator Pipes;
|
||||||
|
typedef proc::play::POutputManager Output;
|
||||||
|
typedef mobject::session::PClipMO Clip;
|
||||||
|
typedef mobject::PTrack Track;
|
||||||
|
typedef asset::PTimeline Timeline;
|
||||||
|
typedef asset::PViewer Viewer;
|
||||||
|
|
||||||
|
/** core operation: create a new playback process
|
||||||
|
* outputting to the given viewer/display */
|
||||||
|
virtual Controller connect(ModelPorts, Output) =0;
|
||||||
|
|
||||||
|
|
||||||
|
/* ==== convenience shortcuts for common use cases ==== */
|
||||||
|
Controller perform(Pipes, Output);
|
||||||
|
Controller perform(Timeline);
|
||||||
|
Controller perform(Viewer);
|
||||||
|
Controller perform(Track);
|
||||||
|
Controller perform(Clip);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~Play();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace lumiera
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#endif /* =========================== CL Interface ===================== */
|
||||||
|
|
||||||
|
|
||||||
|
// #include "common/interface.h"
|
||||||
|
////////////////////////////////////TODO define a C binding for the Interface system here
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
** e.g. throwing an exception instead of creating a NULL value.
|
** e.g. throwing an exception instead of creating a NULL value.
|
||||||
**
|
**
|
||||||
** @see lumiera::WrapperPtr usage example to access a variant record
|
** @see lumiera::WrapperPtr usage example to access a variant record
|
||||||
|
** @see lib::InPlaceAnyHolder usage example to access a subclass in embedded storage
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,7 @@ namespace lumiera {
|
||||||
LUMIERA_ERROR_DECLARE (ASSERTION); ///< assertion failure
|
LUMIERA_ERROR_DECLARE (ASSERTION); ///< assertion failure
|
||||||
|
|
||||||
/* generic error situations */
|
/* generic error situations */
|
||||||
|
LUMIERA_ERROR_DECLARE (LIFECYCLE); ///< Lifecycle assumptions violated
|
||||||
LUMIERA_ERROR_DECLARE (WRONG_TYPE); ///< runtime type mismatch
|
LUMIERA_ERROR_DECLARE (WRONG_TYPE); ///< runtime type mismatch
|
||||||
LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached
|
LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached
|
||||||
LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value
|
LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ namespace lumiera {
|
||||||
LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure");
|
LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure");
|
||||||
|
|
||||||
/* some further generic error situations */
|
/* some further generic error situations */
|
||||||
|
LUMIERA_ERROR_DEFINE (LIFECYCLE, "Lifecycle assumptions violated");
|
||||||
LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch");
|
LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch");
|
||||||
LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached");
|
LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached");
|
||||||
LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value");
|
LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value");
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,15 @@
|
||||||
#define FUNCTOR_UTIL_H_
|
#define FUNCTOR_UTIL_H_
|
||||||
|
|
||||||
#include <tr1/functional>
|
#include <tr1/functional>
|
||||||
#include <cstring>
|
#include <boost/functional/hash.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace util { ////////////TODO: refactor it. But probably not directly into namespace lib. Needs some more consideration though
|
namespace lib {
|
||||||
|
typedef size_t HashVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace util { ////////////TODO: refactor namespace. But probably not directly into namespace lib. Needs some more consideration though
|
||||||
|
|
||||||
using std::tr1::function;
|
using std::tr1::function;
|
||||||
using std::tr1::bind;
|
using std::tr1::bind;
|
||||||
|
|
@ -100,8 +104,11 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
|
||||||
|
|
||||||
namespace { // hiding some nasty details...
|
namespace { // hiding some nasty details...
|
||||||
|
|
||||||
|
using lib::HashVal;
|
||||||
|
using boost::hash_combine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Class is used to surpass the access protection
|
* This Class is used to bypass the access protection
|
||||||
* and break into the tr1::function implementation.
|
* and break into the tr1::function implementation.
|
||||||
* Thus we can implement a raw comparison function,
|
* Thus we can implement a raw comparison function,
|
||||||
* as a replacement for the missing functor comparison
|
* as a replacement for the missing functor comparison
|
||||||
|
|
@ -136,6 +143,16 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
|
||||||
&& (f1._M_functor._M_unused._M_const_object ==
|
&& (f1._M_functor._M_unused._M_const_object ==
|
||||||
f2._M_functor._M_unused._M_const_object );
|
f2._M_functor._M_unused._M_const_object );
|
||||||
} // note: we don't cover any member pointer offset
|
} // note: we don't cover any member pointer offset
|
||||||
|
|
||||||
|
friend HashVal
|
||||||
|
hash_value (HijackedFunction const& fun)
|
||||||
|
{
|
||||||
|
HashVal hash(0);
|
||||||
|
hash_combine (hash, fun.invoker_);
|
||||||
|
hash_combine (hash, fun._M_manager);
|
||||||
|
hash_combine (hash, fun._M_functor._M_unused._M_const_object);
|
||||||
|
return hash; // note: member pointer offset part uncovered
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +201,41 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** workaround to calculate a hash value for a given function object.
|
||||||
|
* @note use with caution. This implementation relies on internal details
|
||||||
|
* of boost/function; it can be expected to be rather conservative,
|
||||||
|
* i.e. yielding different hash values for objects, which actually
|
||||||
|
* are semantically equivalent.
|
||||||
|
* @warning especially function objects bound to member functions aren't
|
||||||
|
* fully supported. It \em may happen that we miss differences on the
|
||||||
|
* offset part and only hash the "this" pointer on some platform.
|
||||||
|
*/
|
||||||
|
template<typename SIG>
|
||||||
|
inline HashVal
|
||||||
|
rawHashValue (function<SIG> const& fun)
|
||||||
|
{
|
||||||
|
typedef HijackedFunction const& Hij;
|
||||||
|
|
||||||
|
return hash_value (reinterpret_cast<Hij> (fun));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
namespace tr1 {
|
||||||
|
|
||||||
|
/** inject into std::tr1 to be picked up by ADL:
|
||||||
|
* @return hash value of given functor
|
||||||
|
* @note use with caution. Hash is calculated
|
||||||
|
* relying on undocumented boost internals.
|
||||||
|
*/
|
||||||
|
template<typename SIG>
|
||||||
|
inline lib::HashVal
|
||||||
|
hash_value (function<SIG> const& fun)
|
||||||
|
{
|
||||||
|
return util::rawHashValue (fun);
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
#endif /*UTIL_HPP_*/
|
#endif /*UTIL_HPP_*/
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace lib {
|
||||||
* Generic opaque reference counting handle, for accessing a service
|
* Generic opaque reference counting handle, for accessing a service
|
||||||
* and managing its lifecycle. Usually such a handle is created by
|
* and managing its lifecycle. Usually such a handle is created by
|
||||||
* an service interface and \link #activate activated \endlink by
|
* an service interface and \link #activate activated \endlink by
|
||||||
* setting up the link to some internal implementation object.
|
* setting up the link to a suitable hidden implementation object.
|
||||||
* This setup can only be done by a friend or derived class, //////////////////////////TODO: that was the intention. Why didn't this work out as expected?
|
* This setup can only be done by a friend or derived class, //////////////////////////TODO: that was the intention. Why didn't this work out as expected?
|
||||||
* while client code is free to copy and store handle objects.
|
* while client code is free to copy and store handle objects.
|
||||||
* Finally, any handle can be closed, thereby decrementing
|
* Finally, any handle can be closed, thereby decrementing
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,14 @@ namespace lib {
|
||||||
typedef typename IterSource<Val>::iterator Iter;
|
typedef typename IterSource<Val>::iterator Iter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class IT, class FUN>
|
||||||
|
struct _TransformIterT
|
||||||
|
{
|
||||||
|
typedef typename _ProducedOutput<FUN>::Type ResVal;
|
||||||
|
typedef TransformIter<IT,ResVal> TransIter;
|
||||||
|
typedef typename IterSource<ResVal>::iterator Iter;
|
||||||
|
};
|
||||||
|
|
||||||
template<class IT>
|
template<class IT>
|
||||||
struct _PairIterT
|
struct _PairIterT
|
||||||
{
|
{
|
||||||
|
|
@ -329,6 +337,28 @@ namespace lib {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** pipes a given Lumiera Forward Iterator through
|
||||||
|
* a transformation function and wraps the resulting
|
||||||
|
* transforming Iterator, exposing just a IterSource.
|
||||||
|
* This convenience shortcut can be used to build a
|
||||||
|
* processing chain; the resulting IterSource will
|
||||||
|
* hide any involved detail types.
|
||||||
|
* @note as with any IterSource, there is one virtual
|
||||||
|
* function call for every fetched element.
|
||||||
|
*/
|
||||||
|
template<class IT, class FUN>
|
||||||
|
typename _TransformIterT<IT,FUN>::Iter
|
||||||
|
transform (IT const& source, FUN processingFunc)
|
||||||
|
{
|
||||||
|
typedef typename _TransformIterT<IT,FUN>::ResVal ValType;
|
||||||
|
typedef typename _TransformIterT<IT,FUN>::TransIter TransIT;
|
||||||
|
|
||||||
|
return IterSource<ValType>::build (
|
||||||
|
new WrappedLumieraIterator<TransIT> (
|
||||||
|
transformIterator (source, processingFunc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @return a Lumiera Forward Iterator to yield
|
/** @return a Lumiera Forward Iterator to yield
|
||||||
* all the keys of the given Map or Hashtable
|
* all the keys of the given Map or Hashtable
|
||||||
*/
|
*/
|
||||||
|
|
@ -424,6 +454,7 @@ namespace lib {
|
||||||
|
|
||||||
}
|
}
|
||||||
using iter_source::wrapIter;
|
using iter_source::wrapIter;
|
||||||
|
using iter_source::transform;
|
||||||
using iter_source::eachMapKey;
|
using iter_source::eachMapKey;
|
||||||
using iter_source::eachDistinctKey;
|
using iter_source::eachDistinctKey;
|
||||||
using iter_source::eachValForKey;
|
using iter_source::eachValForKey;
|
||||||
|
|
|
||||||
|
|
@ -76,11 +76,11 @@ namespace lumiera {
|
||||||
extern "C" { /* ==== implementation C interface for lifecycle hooks ======= */
|
extern "C" { /* ==== implementation C interface for lifecycle hooks ======= */
|
||||||
|
|
||||||
|
|
||||||
extern const char * lumiera_ON_BASIC_INIT = lumiera::ON_BASIC_INIT;
|
const char * lumiera_ON_BASIC_INIT = lumiera::ON_BASIC_INIT;
|
||||||
extern const char * lumiera_ON_GLOBAL_INIT = lumiera::ON_GLOBAL_INIT;
|
const char * lumiera_ON_GLOBAL_INIT = lumiera::ON_GLOBAL_INIT;
|
||||||
extern const char * lumiera_ON_GLOBAL_SHUTDOWN = lumiera::ON_GLOBAL_SHUTDOWN;
|
const char * lumiera_ON_GLOBAL_SHUTDOWN = lumiera::ON_GLOBAL_SHUTDOWN;
|
||||||
|
|
||||||
extern const char * lumiera_ON_EMERGENCY = lumiera::ON_EMERGENCY;
|
const char * lumiera_ON_EMERGENCY = lumiera::ON_EMERGENCY;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
|
|
||||||
/** @file function-erasure.hpp
|
/** @file function-erasure.hpp
|
||||||
|
** Generic holder for functor objects, concealing the concrete function type.
|
||||||
** When working with generic function objects and function pointers typed to
|
** When working with generic function objects and function pointers typed to
|
||||||
** arbitrary signatures, often there is the necessity to hold onto such a functor
|
** arbitrary signatures, often there is the necessity to hold onto such a functor
|
||||||
** while hiding the actual signature behind an common interface ("type erasure").
|
** while hiding the actual signature behind an common interface ("type erasure").
|
||||||
|
|
|
||||||
101
src/lib/meta/generator-combinations.hpp
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
GENERATOR-COMBINATIONS.hpp - generate combinations and variations
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file generator-combinations.hpp
|
||||||
|
** Metaprogramming facilities to generate combination cases.
|
||||||
|
** Similar to the plain typelist-based generators, a custom supplied
|
||||||
|
** template will be instantiated with combinations of the parameter types
|
||||||
|
** and then mixed into the resulting type
|
||||||
|
**
|
||||||
|
** @see generator-combinations-test.cpp
|
||||||
|
** @see generator.hpp
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LUMIERA_META_GENERATOR_COMBINATIONS_H
|
||||||
|
#define LUMIERA_META_GENERATOR_COMBINATIONS_H
|
||||||
|
|
||||||
|
#include "lib/meta/typelist.hpp"
|
||||||
|
#include "lib/meta/typelist-manip.hpp"
|
||||||
|
#include "lib/meta/generator.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace lumiera {
|
||||||
|
namespace typelist{
|
||||||
|
|
||||||
|
|
||||||
|
template<class TYPES_1, class TYPES_2>
|
||||||
|
struct CartesianProduct
|
||||||
|
: Distribute< typename TYPES_1::List
|
||||||
|
, typename TYPES_2::List
|
||||||
|
>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template< template<class,class,class> class _X_>
|
||||||
|
struct PickParametersFromSublist
|
||||||
|
{
|
||||||
|
template<class SUBLIST, class BASE>
|
||||||
|
struct SingleCaseInstantiation
|
||||||
|
: _X_< typename Pick<SUBLIST,0>::Type
|
||||||
|
, typename Pick<SUBLIST,1>::Type
|
||||||
|
, BASE
|
||||||
|
>
|
||||||
|
{ };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a Case matrix.
|
||||||
|
* The given parameter template _X_
|
||||||
|
* will be instantiated for each possible combination
|
||||||
|
* of the elements from both parameter type-lists.
|
||||||
|
* All these instantiations will be chained up
|
||||||
|
* into a linear inheritance chain rooted
|
||||||
|
* at the BASE type.
|
||||||
|
* @note the custom-supplied template _X_ needs to take a 3rd parameter,
|
||||||
|
* and inherit from this parameter, in order to form that chain.
|
||||||
|
* Typically you'll define some (static) functions within that
|
||||||
|
* template, which then forward the call to the given BASE
|
||||||
|
* (and of course, that BASE then needs to define this
|
||||||
|
* function as well).
|
||||||
|
*/
|
||||||
|
template
|
||||||
|
< class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from
|
||||||
|
, template<class,class,class> class _X_ ///< template with two arg types and a base type
|
||||||
|
, class BASE = NullType
|
||||||
|
>
|
||||||
|
struct InstantiateChainedCombinations
|
||||||
|
: InstantiateChained< typename CartesianProduct<TYPES_1,TYPES_2>::List
|
||||||
|
, PickParametersFromSublist<_X_>::template SingleCaseInstantiation
|
||||||
|
, BASE
|
||||||
|
>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}} // namespace lumiera::typelist
|
||||||
|
#endif
|
||||||
329
src/lib/meta/typelist-manip.hpp
Normal file
|
|
@ -0,0 +1,329 @@
|
||||||
|
/*
|
||||||
|
TYPELIST-MANIP.hpp - Utils for working with lists-of-types
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file typelist-manip.hpp
|
||||||
|
** Metaprogramming: Helpers for manipulating lists-of-types.
|
||||||
|
** Sometimes, we use metaprogramming to generate a variation of concrete
|
||||||
|
** implementations by combining some basic building blocks. Typically, there
|
||||||
|
** is a number of similar, but not suitably related types involved. We want to
|
||||||
|
** process those types using a common scheme, without being forced to squeeze
|
||||||
|
** all those types into a artificial inheritance relationship. Now, generating
|
||||||
|
** some kind of common factory or adapter, while mixing in pieces of code tailored
|
||||||
|
** specifically to the individual types, allows still to build a common processing
|
||||||
|
** in such situations.
|
||||||
|
**
|
||||||
|
** The facilities in this header provide the basics of simple functional list
|
||||||
|
** processing (mostly with tail recursion). Usually, there is one template parameter
|
||||||
|
** TYPES, which accepts a \em Type-list. The result of the processing step is then
|
||||||
|
** accessible as an embedded typedef named \c List . Here, all of the 'processing'
|
||||||
|
** to calculate this result is performed by the compiler, as a side-effect of
|
||||||
|
** figuring out the resulting concrete type. At run time, in the generated
|
||||||
|
** code, typically the resulting classes are empty, maybe just
|
||||||
|
** exposing a specifically built-up function.
|
||||||
|
**
|
||||||
|
** @see generator.hpp
|
||||||
|
** @see typelist-manip-test.cpp
|
||||||
|
** @see TimeControl_test usage example
|
||||||
|
** @see typelist.hpp
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LUMIERA_META_TYPELIST_MANIP_H
|
||||||
|
#define LUMIERA_META_TYPELIST_MANIP_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/meta/typelist.hpp"
|
||||||
|
|
||||||
|
namespace lumiera {
|
||||||
|
namespace typelist{
|
||||||
|
|
||||||
|
|
||||||
|
/** pick the n-th element from a typelist */
|
||||||
|
template<class TYPES, uint i>
|
||||||
|
struct Pick
|
||||||
|
{
|
||||||
|
typedef NullType Type;
|
||||||
|
};
|
||||||
|
template<class TY, class TYPES>
|
||||||
|
struct Pick<Node<TY,TYPES>, 0>
|
||||||
|
{
|
||||||
|
typedef TY Type;
|
||||||
|
};
|
||||||
|
template<class TY, class TYPES, uint i>
|
||||||
|
struct Pick<Node<TY,TYPES>, i>
|
||||||
|
{
|
||||||
|
typedef typename Pick<TYPES, i-1>::Type Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** apply a transformation (template) to each type in the list */
|
||||||
|
template<class TY, template<class> class _TRANS_>
|
||||||
|
struct Apply { typedef TY List; };
|
||||||
|
|
||||||
|
template< class TY, class TYPES
|
||||||
|
, template<class> class _TRANS_
|
||||||
|
>
|
||||||
|
struct Apply<Node<TY,TYPES>, _TRANS_ > { typedef Node< typename _TRANS_<TY>::Type
|
||||||
|
, typename Apply<TYPES,_TRANS_>::List
|
||||||
|
> List; };
|
||||||
|
|
||||||
|
|
||||||
|
/** conditional node: skip an element based on evaluating a predicate */
|
||||||
|
template<bool, class T, class TAIL>
|
||||||
|
struct CondNode { typedef TAIL Next; };
|
||||||
|
|
||||||
|
template<class T, class TAIL>
|
||||||
|
struct CondNode<true, T, TAIL> { typedef Node<T,TAIL> Next; };
|
||||||
|
|
||||||
|
/** filter away those types which don't fulfil a predicate metafunction */
|
||||||
|
template< class TYPES
|
||||||
|
, template<class> class _P_ ///< a template providing a boolean member \c ::value
|
||||||
|
>
|
||||||
|
struct Filter;
|
||||||
|
|
||||||
|
template<template<class> class _P_>
|
||||||
|
struct Filter<NullType,_P_> { typedef NullType List; };
|
||||||
|
|
||||||
|
template< class TY, class TYPES
|
||||||
|
, template<class> class _P_
|
||||||
|
>
|
||||||
|
struct Filter<Node<TY,TYPES>,_P_> { typedef typename CondNode< _P_<TY>::value
|
||||||
|
, TY
|
||||||
|
, typename Filter<TYPES,_P_>::List
|
||||||
|
>::Next
|
||||||
|
List; };
|
||||||
|
|
||||||
|
|
||||||
|
/** append lists-of-types */
|
||||||
|
template<class TY1, class TY2>
|
||||||
|
struct Append { typedef Node<TY1, typename Append<TY2,NullType>::List> List; };
|
||||||
|
|
||||||
|
template< class TY, class TYPES
|
||||||
|
, class TAIL
|
||||||
|
>
|
||||||
|
struct Append<Node<TY,TYPES>, TAIL> { typedef Node<TY, typename Append<TYPES, TAIL>::List> List; };
|
||||||
|
|
||||||
|
template<class TY, class TYPES>
|
||||||
|
struct Append<NullType, Node<TY,TYPES> > { typedef Node<TY,TYPES> List; };
|
||||||
|
|
||||||
|
template<class TY, class TYPES>
|
||||||
|
struct Append<Node<TY,TYPES>, NullType> { typedef Node<TY,TYPES> List; };
|
||||||
|
|
||||||
|
template<class TY1>
|
||||||
|
struct Append<TY1,NullType> { typedef Node<TY1,NullType> List; };
|
||||||
|
|
||||||
|
template<class TY2>
|
||||||
|
struct Append<NullType,TY2> { typedef Node<TY2,NullType> List; };
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Append<NullType,NullType> { typedef NullType List; };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** access the last list element */
|
||||||
|
template<class TYPES>
|
||||||
|
struct SplitLast;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct SplitLast<NullType> { typedef NullType Type;
|
||||||
|
typedef NullType List; };
|
||||||
|
template<class TY>
|
||||||
|
struct SplitLast<Node<TY,NullType> > { typedef TY Type;
|
||||||
|
typedef NullType List; };
|
||||||
|
|
||||||
|
template<class TY, class TYPES>
|
||||||
|
struct SplitLast<Node<TY,TYPES> > { typedef typename SplitLast<TYPES>::Type Type;
|
||||||
|
typedef typename Append< TY,
|
||||||
|
typename SplitLast<TYPES>::List
|
||||||
|
>::List
|
||||||
|
List; };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* splice a typelist like an overlay
|
||||||
|
* into an base typelist, starting at given index.
|
||||||
|
* @return either the combined (spliced) List, or
|
||||||
|
* the Front/Back part before or after the Overlay
|
||||||
|
* @note using a NullType as OVERLAY allows to extract
|
||||||
|
* an arbitrary Front/Back part of the list
|
||||||
|
*/
|
||||||
|
template<class BASE, class OVERLAY, uint i=0>
|
||||||
|
struct Splice;
|
||||||
|
|
||||||
|
template<class B, class BS,
|
||||||
|
class OVERLAY, uint i>
|
||||||
|
struct Splice<Node<B,BS>, OVERLAY, i> { typedef Node<B, typename Splice<BS, OVERLAY, i-1>::List> List;
|
||||||
|
typedef Node<B, typename Splice<BS, OVERLAY, i-1>::Front> Front;
|
||||||
|
typedef typename Splice<BS, OVERLAY, i-1>::Back Back; };
|
||||||
|
|
||||||
|
template<class B, class BS,
|
||||||
|
class O, class OS >
|
||||||
|
struct Splice<Node<B,BS>,Node<O,OS>,0> { typedef Node<O, typename Splice<BS,OS, 0>::List> List;
|
||||||
|
typedef NullType Front;
|
||||||
|
typedef typename Splice<BS,OS, 0>::Back Back; };
|
||||||
|
|
||||||
|
template<class B, class BS>
|
||||||
|
struct Splice<Node<B,BS>, NullType, 0> { typedef Node<B, BS> List;
|
||||||
|
typedef NullType Front;
|
||||||
|
typedef Node<B, BS> Back; };
|
||||||
|
|
||||||
|
template<class XX, uint i>
|
||||||
|
struct Splice<NullType, XX, i> { typedef NullType List;
|
||||||
|
typedef NullType Front;
|
||||||
|
typedef NullType Back; };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to access various parts of a given typelist:
|
||||||
|
* Start and End, Prefix and Tail...
|
||||||
|
*/
|
||||||
|
template<class TYPES>
|
||||||
|
struct Dissect;
|
||||||
|
|
||||||
|
template<class T, class TYPES>
|
||||||
|
struct Dissect<Node<T,TYPES> >
|
||||||
|
{
|
||||||
|
typedef Node<T,TYPES> List; ///< the complete list
|
||||||
|
typedef T Head; ///< first element
|
||||||
|
typedef Node<T,NullType> First; ///< a list containing the first element
|
||||||
|
typedef TYPES Tail; ///< remainder of the list starting with the second elm.
|
||||||
|
typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but excluding the last element
|
||||||
|
typedef typename SplitLast<List>::Type End; ///< the last element
|
||||||
|
typedef Node<End,NullType> Last; ///< a list containing the last element
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Dissect<NullType>
|
||||||
|
{
|
||||||
|
typedef NullType List;
|
||||||
|
typedef NullType Head;
|
||||||
|
typedef NullType First;
|
||||||
|
typedef NullType Tail;
|
||||||
|
typedef NullType Prefix;
|
||||||
|
typedef NullType End;
|
||||||
|
typedef NullType Last;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prefix each of the elements,
|
||||||
|
* yielding a list-of lists-of-types
|
||||||
|
*/
|
||||||
|
template<class T, class TY>
|
||||||
|
struct PrefixAll { typedef Node< typename Append<T,TY>::List, NullType> List; };
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct PrefixAll<T, NullType> { typedef NullType List; };
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct PrefixAll<T, NodeNull> { typedef Node< typename Append<T,NodeNull>::List, NullType> List; };
|
||||||
|
|
||||||
|
template< class T
|
||||||
|
, class TY, class TYPES
|
||||||
|
>
|
||||||
|
struct PrefixAll<T, Node<TY,TYPES> > { typedef Node< typename Append<T,TY>::List
|
||||||
|
, typename PrefixAll<T,TYPES>::List
|
||||||
|
> List; };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* build a list-of lists, where each element of the first arg list
|
||||||
|
* gets in turn prepended to all elements of the second arg list.
|
||||||
|
* Can be used to build all possible combinations from two
|
||||||
|
* sources, i.e. the Cartesian product.
|
||||||
|
*/
|
||||||
|
template<class TY1,class TY2>
|
||||||
|
struct Distribute { typedef typename PrefixAll<TY1,TY2>::List List; };
|
||||||
|
|
||||||
|
template<class TY>
|
||||||
|
struct Distribute<NullType,TY> { typedef NullType List; };
|
||||||
|
|
||||||
|
template< class TY, class TYPES
|
||||||
|
, class TAIL
|
||||||
|
>
|
||||||
|
struct Distribute<Node<TY,TYPES>,TAIL> { typedef typename Append< typename PrefixAll<TY,TAIL>::List
|
||||||
|
, typename Distribute<TYPES,TAIL>::List
|
||||||
|
>::List
|
||||||
|
List; };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* build all possible combinations, based on a enumeration of the basic cases.
|
||||||
|
* For each of the types in the argument list, an "enumeration generator" template is invoked,
|
||||||
|
* yielding a list of the possible base cases. These base cases are then combined with all the
|
||||||
|
* combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered"
|
||||||
|
* means that the base cases of the n-th element will appear in the n-th position of the
|
||||||
|
* resulting lists,
|
||||||
|
*
|
||||||
|
* For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a
|
||||||
|
* list-of-lists, featuring all possibilities to combine these distinct toggles.
|
||||||
|
*/
|
||||||
|
template< class X
|
||||||
|
, template<class> class _ENUM_>
|
||||||
|
struct Combine { typedef typename Distribute< typename _ENUM_<X>::List
|
||||||
|
, Node<NullType,NullType>
|
||||||
|
>::List List; };
|
||||||
|
template< template<class> class _ENUM_>
|
||||||
|
struct Combine<NullType, _ENUM_ > { typedef NodeNull List; };
|
||||||
|
|
||||||
|
template< class TY, class TYPES
|
||||||
|
, template<class> class _ENUM_>
|
||||||
|
struct Combine<Node<TY,TYPES>,_ENUM_> { typedef typename Distribute< typename _ENUM_<TY>::List
|
||||||
|
, typename Combine<TYPES,_ENUM_>::List
|
||||||
|
>::List List; };
|
||||||
|
|
||||||
|
/** enumeration generator for the Combine metafunction,
|
||||||
|
* yielding an "on" and "off" case
|
||||||
|
*/
|
||||||
|
template<class F>
|
||||||
|
struct FlagOnOff
|
||||||
|
{
|
||||||
|
typedef Node<F, Node<NullType,NullType> > List;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** generate all possible on-off combinations of the given flags */
|
||||||
|
template<class FLAGS>
|
||||||
|
struct CombineFlags
|
||||||
|
{
|
||||||
|
typedef typename Combine<FLAGS, FlagOnOff>::List List;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}} // namespace lumiera::typelist
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
TYPELIST-UTIL.hpp - Utils for working with lists-of-types
|
TYPELIST-UTIL.hpp - simple helpers for working with lists-of-types
|
||||||
|
|
||||||
Copyright (C) Lumiera.org
|
Copyright (C) Lumiera.org
|
||||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
@ -21,6 +21,27 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file typelist-util.hpp
|
||||||
|
** Metaprogramming: simple helpers for working with lists-of-types.
|
||||||
|
** This header provides some very basic "meta functions" for extracting
|
||||||
|
** some informations from a list-of-types. In Lumiera, we use template
|
||||||
|
** metaprogramming and especially such lists-of-types, whenever we build
|
||||||
|
** some common implementation backbone, without being able to subsume all
|
||||||
|
** participating types (classes) into a single inheritance hierarchy.
|
||||||
|
**
|
||||||
|
** The "meta functions" defined here are templates; to access the "result" of
|
||||||
|
** such a meta function, we instantiate the template and then access one of the
|
||||||
|
** embedded constant definitions (usually the enum constant named \c value)
|
||||||
|
**
|
||||||
|
** @see generator.hpp
|
||||||
|
** @see TypelistUtil_test
|
||||||
|
** @see lib::SimpleAllocator usage example (for isInList)
|
||||||
|
** @see TypelistManip_test
|
||||||
|
** @see typelist.hpp
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef LUMIERA_META_TYPELIST_UTIL_H
|
#ifndef LUMIERA_META_TYPELIST_UTIL_H
|
||||||
#define LUMIERA_META_TYPELIST_UTIL_H
|
#define LUMIERA_META_TYPELIST_UTIL_H
|
||||||
|
|
||||||
|
|
@ -34,6 +55,7 @@ namespace typelist{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metafunction counting the number of Types in the collection
|
* Metafunction counting the number of Types in the collection
|
||||||
|
* @return an embedded constant \c value holding the result
|
||||||
*/
|
*/
|
||||||
template<class TYPES>
|
template<class TYPES>
|
||||||
struct count;
|
struct count;
|
||||||
|
|
@ -48,6 +70,7 @@ namespace typelist{
|
||||||
enum{ value = 1 + count<TYPES>::value };
|
enum{ value = 1 + count<TYPES>::value };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metafunction " max( sizeof(T) ) for T in TYPES "
|
* Metafunction " max( sizeof(T) ) for T in TYPES "
|
||||||
*/
|
*/
|
||||||
|
|
@ -68,233 +91,35 @@ namespace typelist{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** apply a transformation (template) to each type in the list */
|
|
||||||
template<class TY, template<class> class _TRANS_>
|
|
||||||
struct Apply { typedef TY List; };
|
|
||||||
|
|
||||||
template< class TY, class TYPES
|
|
||||||
, template<class> class _TRANS_
|
|
||||||
>
|
|
||||||
struct Apply<Node<TY,TYPES>, _TRANS_ > { typedef Node< typename _TRANS_<TY>::Type
|
|
||||||
, typename Apply<TYPES,_TRANS_>::List
|
|
||||||
> List; };
|
|
||||||
|
|
||||||
|
|
||||||
template<bool, class T, class TAIL>
|
|
||||||
struct CondNode { typedef TAIL Next; };
|
|
||||||
|
|
||||||
template<class T, class TAIL>
|
|
||||||
struct CondNode<true, T, TAIL> { typedef Node<T,TAIL> Next; };
|
|
||||||
|
|
||||||
/** filter away those types which don't fulfil a predicate metafunction */
|
|
||||||
template< class TYPES
|
|
||||||
, template<class> class _P_ ///< a template providing a boolean member \c ::value
|
|
||||||
>
|
|
||||||
struct Filter;
|
|
||||||
|
|
||||||
template<template<class> class _P_>
|
|
||||||
struct Filter<NullType,_P_> { typedef NullType List; };
|
|
||||||
|
|
||||||
template< class TY, class TYPES
|
|
||||||
, template<class> class _P_
|
|
||||||
>
|
|
||||||
struct Filter<Node<TY,TYPES>,_P_> { typedef typename CondNode< _P_<TY>::value
|
|
||||||
, TY
|
|
||||||
, typename Filter<TYPES,_P_>::List
|
|
||||||
>::Next
|
|
||||||
List; };
|
|
||||||
|
|
||||||
|
|
||||||
/** append lists-of-types */
|
|
||||||
template<class TY1, class TY2>
|
|
||||||
struct Append { typedef Node<TY1, typename Append<TY2,NullType>::List> List; };
|
|
||||||
|
|
||||||
template< class TY, class TYPES
|
|
||||||
, class TAIL
|
|
||||||
>
|
|
||||||
struct Append<Node<TY,TYPES>, TAIL> { typedef Node<TY, typename Append<TYPES, TAIL>::List> List; };
|
|
||||||
|
|
||||||
template<class TY, class TYPES>
|
|
||||||
struct Append<NullType, Node<TY,TYPES> > { typedef Node<TY,TYPES> List; };
|
|
||||||
|
|
||||||
template<class TY, class TYPES>
|
|
||||||
struct Append<Node<TY,TYPES>, NullType> { typedef Node<TY,TYPES> List; };
|
|
||||||
|
|
||||||
template<class TY1>
|
|
||||||
struct Append<TY1,NullType> { typedef Node<TY1,NullType> List; };
|
|
||||||
|
|
||||||
template<class TY2>
|
|
||||||
struct Append<NullType,TY2> { typedef Node<TY2,NullType> List; };
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct Append<NullType,NullType> { typedef NullType List; };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** access the last list element */
|
|
||||||
template<class TYPES>
|
|
||||||
struct SplitLast;
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct SplitLast<NullType> { typedef NullType Type;
|
|
||||||
typedef NullType List; };
|
|
||||||
template<class TY>
|
|
||||||
struct SplitLast<Node<TY,NullType> > { typedef TY Type;
|
|
||||||
typedef NullType List; };
|
|
||||||
|
|
||||||
template<class TY, class TYPES>
|
|
||||||
struct SplitLast<Node<TY,TYPES> > { typedef typename SplitLast<TYPES>::Type Type;
|
|
||||||
typedef typename Append< TY,
|
|
||||||
typename SplitLast<TYPES>::List
|
|
||||||
>::List
|
|
||||||
List; };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* splice a typelist like an overlay
|
* Metafunction to check if a specific type is contained
|
||||||
* into an base typelist, starting at given index.
|
* in a given typelist. Only exact match is detected.
|
||||||
* @return either the combined (spliced) List, or
|
|
||||||
* the Front/Back part before or after the Overlay
|
|
||||||
* @note using a NullType as OVERLAY allows to extract
|
|
||||||
* an arbitrary Front/Back part of the list
|
|
||||||
*/
|
*/
|
||||||
template<class BASE, class OVERLAY, uint i=0>
|
template<typename TY, typename TYPES>
|
||||||
struct Splice;
|
struct IsInList
|
||||||
|
|
||||||
template<class B, class BS,
|
|
||||||
class OVERLAY, uint i>
|
|
||||||
struct Splice<Node<B,BS>, OVERLAY, i> { typedef Node<B, typename Splice<BS, OVERLAY, i-1>::List> List;
|
|
||||||
typedef Node<B, typename Splice<BS, OVERLAY, i-1>::Front> Front;
|
|
||||||
typedef typename Splice<BS, OVERLAY, i-1>::Back Back; };
|
|
||||||
|
|
||||||
template<class B, class BS,
|
|
||||||
class O, class OS >
|
|
||||||
struct Splice<Node<B,BS>,Node<O,OS>,0> { typedef Node<O, typename Splice<BS,OS, 0>::List> List;
|
|
||||||
typedef NullType Front;
|
|
||||||
typedef typename Splice<BS,OS, 0>::Back Back; };
|
|
||||||
|
|
||||||
template<class B, class BS>
|
|
||||||
struct Splice<Node<B,BS>, NullType, 0> { typedef Node<B, BS> List;
|
|
||||||
typedef NullType Front;
|
|
||||||
typedef Node<B, BS> Back; };
|
|
||||||
|
|
||||||
template<class XX, uint i>
|
|
||||||
struct Splice<NullType, XX, i> { typedef NullType List;
|
|
||||||
typedef NullType Front;
|
|
||||||
typedef NullType Back; };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows to access various parts of a given typelist:
|
|
||||||
* Start and End, Prefix and Tail..
|
|
||||||
*/
|
|
||||||
template<class TYPES>
|
|
||||||
struct Dissect;
|
|
||||||
|
|
||||||
template<class T, class TYPES>
|
|
||||||
struct Dissect<Node<T,TYPES> >
|
|
||||||
{
|
{
|
||||||
typedef Node<T,TYPES> List; ///< the complete list
|
enum{ value = false };
|
||||||
typedef T Head; ///< first element
|
|
||||||
typedef Node<T,NullType> First; ///< a list containing the first element
|
|
||||||
typedef TYPES Tail; ///< remainder of the list starting with the second elm.
|
|
||||||
typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but extcluding the last element
|
|
||||||
typedef typename SplitLast<List>::Type End; ///< the last element
|
|
||||||
typedef Node<End,NullType> Last; ///< a list containing the last element
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<typename TY, typename TYPES>
|
||||||
struct Dissect<NullType>
|
struct IsInList<TY, Node<TY,TYPES> >
|
||||||
{
|
{
|
||||||
typedef NullType List;
|
enum{ value = true };
|
||||||
typedef NullType Head;
|
|
||||||
typedef NullType First;
|
|
||||||
typedef NullType Tail;
|
|
||||||
typedef NullType Prefix;
|
|
||||||
typedef NullType End;
|
|
||||||
typedef NullType Last;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename TY, typename XX, typename TYPES>
|
||||||
|
struct IsInList<TY, Node<XX,TYPES> >
|
||||||
|
|
||||||
/**
|
|
||||||
* prefix each of the elements,
|
|
||||||
* yielding a list-of lists-of-types
|
|
||||||
*/
|
|
||||||
template<class T, class TY>
|
|
||||||
struct PrefixAll { typedef Node< typename Append<T,TY>::List, NullType> List; };
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
struct PrefixAll<T, NullType> { typedef NullType List; };
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
struct PrefixAll<T, NodeNull> { typedef Node< typename Append<T,NodeNull>::List, NullType> List; };
|
|
||||||
|
|
||||||
template< class T
|
|
||||||
, class TY, class TYPES
|
|
||||||
>
|
|
||||||
struct PrefixAll<T, Node<TY,TYPES> > { typedef Node< typename Append<T,TY>::List
|
|
||||||
, typename PrefixAll<T,TYPES>::List
|
|
||||||
> List; };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<class TY1,class TY2>
|
|
||||||
struct Distribute { typedef typename PrefixAll<TY1,TY2>::List List; };
|
|
||||||
|
|
||||||
template<class TY>
|
|
||||||
struct Distribute<NullType,TY> { typedef NullType List; };
|
|
||||||
|
|
||||||
template< class TY, class TYPES
|
|
||||||
, class TAIL
|
|
||||||
>
|
|
||||||
struct Distribute<Node<TY,TYPES>,TAIL> { typedef typename Append< typename PrefixAll<TY,TAIL>::List
|
|
||||||
, typename Distribute<TYPES,TAIL>::List
|
|
||||||
>::List
|
|
||||||
List; };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** use a permutation generator
|
|
||||||
* for creating a list of all possible combinations
|
|
||||||
*/
|
|
||||||
template< class X
|
|
||||||
, template<class> class _PERMU_>
|
|
||||||
struct Combine { typedef typename Distribute< typename _PERMU_<X>::List
|
|
||||||
, Node<NullType,NullType>
|
|
||||||
>::List List; };
|
|
||||||
template< template<class> class _PERMU_>
|
|
||||||
struct Combine<NullType, _PERMU_ > { typedef Node<NullType,NullType> List; };
|
|
||||||
|
|
||||||
template< class TY, class TYPES
|
|
||||||
, template<class> class _PERMU_>
|
|
||||||
struct Combine<Node<TY,TYPES>,_PERMU_> { typedef typename Distribute< typename _PERMU_<TY>::List
|
|
||||||
, typename Combine<TYPES,_PERMU_>::List
|
|
||||||
>::List List; };
|
|
||||||
|
|
||||||
/** permutation generator for the Combine metafunction,
|
|
||||||
* yielding an "on" and "off" case
|
|
||||||
*/
|
|
||||||
template<class F>
|
|
||||||
struct FlagOnOff
|
|
||||||
{
|
{
|
||||||
typedef Node<F, Node<NullType,NullType> > List;
|
enum{ value = IsInList<TY,TYPES>::value };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** convenience shortcut: query function */
|
||||||
/** generate all possible on-off combinations of the given flags */
|
template<typename TY, typename TYPES>
|
||||||
template<class FLAGS>
|
bool
|
||||||
struct CombineFlags
|
isInList()
|
||||||
{
|
{
|
||||||
typedef typename Combine<FLAGS, FlagOnOff>::List List;
|
return IsInList<TY,TYPES>::value;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,15 +40,25 @@ This code is heavily inspired by
|
||||||
|
|
||||||
/** @file typelist.hpp
|
/** @file typelist.hpp
|
||||||
** A template metaprogramming technique for manipulating collections of types.
|
** A template metaprogramming technique for manipulating collections of types.
|
||||||
** Effectively this is a taylored and simplified version of what can be found in the Loki library.
|
** Effectively this is a tailored and simplified version of what can be found in the Loki library.
|
||||||
** We use it in other generic library-style code to generate repetitive code. If you tend to find
|
** We use it in other generic library-style code to generate repetitive code. If you tend to find
|
||||||
** template metaprogramming (or functional programming in general) offending, please ignore the
|
** template metaprogramming (or functional programming in general) offending, please ignore the
|
||||||
** technical details and just consider the benefit of such an simplification for the user code.
|
** technical details and just consider the benefit of such an simplification for the client code.
|
||||||
**
|
**
|
||||||
** Interface for using this facility is the template Types(.....) for up to 20 Type parameters
|
** Interface for using this facility is the template Types(.....) for up to 20 Type parameters.
|
||||||
|
** To start typelist processing, other templates typically pick up the Types<...>::List type.
|
||||||
|
** This allows for LISP-style list processing, with a pattern match on either Node<TY,TYPES>
|
||||||
|
** or NullType to terminate recursion. In C++ template metaprogramming, "pattern match"
|
||||||
|
** is done by partial template specialisations (the compiler will pick up and thus
|
||||||
|
** match the template parameters). A typedef acts like a declaration in normal
|
||||||
|
** programming. Because such a "declaration" can't be changed after the fact,
|
||||||
|
** effectively this is a flavour of functional programming. Just the
|
||||||
|
** "execution environment" is the compiler, during compilation.
|
||||||
**
|
**
|
||||||
** @see lumiera::visitor::Applicable usage example
|
** @see lumiera::visitor::Applicable usage example
|
||||||
** @see typelisttest.cpp
|
** @see control::CommandSignature more elaborate usage example (dissecting a functor signature)
|
||||||
|
** @see TypeList_test
|
||||||
|
** @see TypeListManip_test
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
#define LUMIERA_META_TYPESEQ_UTIL_H
|
#define LUMIERA_META_TYPESEQ_UTIL_H
|
||||||
|
|
||||||
#include "lib/meta/typelist.hpp"
|
#include "lib/meta/typelist.hpp"
|
||||||
#include "lib/meta/typelist-util.hpp"
|
#include "lib/meta/typelist-manip.hpp"
|
||||||
#include "lib/meta/util.hpp"
|
#include "lib/meta/util.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,24 @@ namespace lumiera {
|
||||||
|
|
||||||
namespace typelist {
|
namespace typelist {
|
||||||
|
|
||||||
|
/** Compile-time Type equality:
|
||||||
|
* Simple Trait template to pick up types considered
|
||||||
|
* \em identical by the compiler.
|
||||||
|
* @warning identical, not sub-type!
|
||||||
|
*/
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
struct is_sameType
|
||||||
|
{
|
||||||
|
static const bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_sameType<T,T>
|
||||||
|
{
|
||||||
|
static const bool value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/** semi-automatic detection if an instantiation is possible.
|
/** semi-automatic detection if an instantiation is possible.
|
||||||
* Requires help by the template to be tested, which needs to define
|
* Requires help by the template to be tested, which needs to define
|
||||||
* a typedef member \c is_defined. The embedded metafunction Test can be used
|
* a typedef member \c is_defined. The embedded metafunction Test can be used
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ namespace lib {
|
||||||
char content_[siz];
|
char content_[siz];
|
||||||
void* ptr() { return &content_; }
|
void* ptr() { return &content_; }
|
||||||
|
|
||||||
virtual ~Buffer() {}
|
virtual ~Buffer() {} ///< this is an ABC with VTable
|
||||||
virtual bool isValid() const =0;
|
virtual bool isValid() const =0;
|
||||||
virtual bool empty() const =0;
|
virtual bool empty() const =0;
|
||||||
virtual BaseP getBase() const =0;
|
virtual BaseP getBase() const =0;
|
||||||
|
|
|
||||||
|
|
@ -389,21 +389,21 @@ namespace lib {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class IMP, typename A1>
|
template<class IMP, typename A1>
|
||||||
PolymorphicValue (IMP*, A1& a1)
|
PolymorphicValue (IMP*, A1 a1)
|
||||||
{
|
{
|
||||||
REQUIRE (siz >= sizeof(IMP));
|
REQUIRE (siz >= sizeof(IMP));
|
||||||
new(&buf_) IMP (a1);
|
new(&buf_) IMP (a1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class IMP, typename A1, typename A2>
|
template<class IMP, typename A1, typename A2>
|
||||||
PolymorphicValue (IMP*, A1& a1, A2& a2)
|
PolymorphicValue (IMP*, A1 a1, A2 a2)
|
||||||
{
|
{
|
||||||
REQUIRE (siz >= sizeof(IMP));
|
REQUIRE (siz >= sizeof(IMP));
|
||||||
new(&buf_) IMP (a1,a2);
|
new(&buf_) IMP (a1,a2);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class IMP, typename A1, typename A2, typename A3>
|
template<class IMP, typename A1, typename A2, typename A3>
|
||||||
PolymorphicValue (IMP*, A1& a1, A2& a2, A3& a3)
|
PolymorphicValue (IMP*, A1 a1, A2 a2, A3 a3)
|
||||||
{
|
{
|
||||||
REQUIRE (siz >= sizeof(IMP));
|
REQUIRE (siz >= sizeof(IMP));
|
||||||
new(&buf_) IMP (a1,a2,a3);
|
new(&buf_) IMP (a1,a2,a3);
|
||||||
|
|
@ -444,13 +444,13 @@ namespace lib {
|
||||||
Adapter() : IMP() { }
|
Adapter() : IMP() { }
|
||||||
|
|
||||||
template<typename A1>
|
template<typename A1>
|
||||||
Adapter (A1& a1) : IMP(a1) { }
|
Adapter (A1 a1) : IMP(a1) { }
|
||||||
|
|
||||||
template<typename A1, typename A2>
|
template<typename A1, typename A2>
|
||||||
Adapter (A1& a1, A2& a2) : IMP(a1,a2) { }
|
Adapter (A1 a1, A2 a2) : IMP(a1,a2) { }
|
||||||
|
|
||||||
template<typename A1, typename A2, typename A3>
|
template<typename A1, typename A2, typename A3>
|
||||||
Adapter (A1& a1, A2& a2, A3& a3) : IMP(a1,a2,a3) { }
|
Adapter (A1 a1, A2 a2, A3 a3) : IMP(a1,a2,a3) { }
|
||||||
|
|
||||||
/* using default copy and assignment */
|
/* using default copy and assignment */
|
||||||
};
|
};
|
||||||
|
|
@ -505,31 +505,31 @@ namespace lib {
|
||||||
static PolymorphicValue
|
static PolymorphicValue
|
||||||
build ()
|
build ()
|
||||||
{
|
{
|
||||||
Adapter<IMP>* type_to_build_in_buffer;
|
Adapter<IMP>* type_to_build_in_buffer(0);
|
||||||
return PolymorphicValue (type_to_build_in_buffer);
|
return PolymorphicValue (type_to_build_in_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class IMP, typename A1>
|
template<class IMP, typename A1>
|
||||||
static PolymorphicValue
|
static PolymorphicValue
|
||||||
build (A1& a1)
|
build (A1 a1)
|
||||||
{
|
{
|
||||||
Adapter<IMP>* type_to_build_in_buffer;
|
Adapter<IMP>* type_to_build_in_buffer(0);
|
||||||
return PolymorphicValue (type_to_build_in_buffer, a1);
|
return PolymorphicValue (type_to_build_in_buffer, a1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class IMP, typename A1, typename A2>
|
template<class IMP, typename A1, typename A2>
|
||||||
static PolymorphicValue
|
static PolymorphicValue
|
||||||
build (A1& a1, A2& a2)
|
build (A1 a1, A2 a2)
|
||||||
{
|
{
|
||||||
Adapter<IMP>* type_to_build_in_buffer;
|
Adapter<IMP>* type_to_build_in_buffer(0);
|
||||||
return PolymorphicValue (type_to_build_in_buffer, a1,a2);
|
return PolymorphicValue (type_to_build_in_buffer, a1,a2);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class IMP, typename A1, typename A2, typename A3>
|
template<class IMP, typename A1, typename A2, typename A3>
|
||||||
static PolymorphicValue
|
static PolymorphicValue
|
||||||
build (A1& a1, A2& a2, A3& a3)
|
build (A1 a1, A2 a2, A3 a3)
|
||||||
{
|
{
|
||||||
Adapter<IMP>* type_to_build_in_buffer;
|
Adapter<IMP>* type_to_build_in_buffer(0);
|
||||||
return PolymorphicValue (type_to_build_in_buffer, a1,a2,a3);
|
return PolymorphicValue (type_to_build_in_buffer, a1,a2,a3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@
|
||||||
/** @file result.hpp
|
/** @file result.hpp
|
||||||
** Intermediary value object to represent the result of an operation.
|
** Intermediary value object to represent the result of an operation.
|
||||||
** This operation might have produced a value result or failed with an exception.
|
** This operation might have produced a value result or failed with an exception.
|
||||||
** Typically, the Result token used \em inline -- immediately either invoking one
|
** Typically, the Result token is used \em inline -- immediately either invoking
|
||||||
** of the member function or employing the built-in result type conversion. It
|
** one of the member function or employing the built-in result type conversion.
|
||||||
** will be copyable iff the result value is copyable. There is an implicit
|
** It will be copyable iff the result value is copyable. There is an implicit
|
||||||
** valid or failure state, which can be tested. Any attempt to get the value
|
** valid or failure state, which can be tested. Any attempt to get the value
|
||||||
** of an invalid result token will cause in an exception to be thrown.
|
** of an invalid result token will cause in an exception to be thrown.
|
||||||
**
|
**
|
||||||
|
|
@ -58,11 +58,10 @@ namespace lib {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result value and status of some operation.
|
* Optional Result value or status of some operation.
|
||||||
* It can be created for passing a result produced
|
* It can be created for passing a result produced by the operation, or the
|
||||||
* by the operation, or the failure to do so. The value
|
* failure to do so. The value can be retrieved by implicit or explicit conversion.
|
||||||
* can be retrieved by implicit or explicit conversion.
|
* @throw error::State on any attempt to access the value in case of failure
|
||||||
* @throws on any attempt to access the value in case of failure
|
|
||||||
* @warning this class has a lot of implicit conversions;
|
* @warning this class has a lot of implicit conversions;
|
||||||
* care should be taken when defining functions
|
* care should be taken when defining functions
|
||||||
* to take Result instances as parameter....
|
* to take Result instances as parameter....
|
||||||
|
|
|
||||||
|
|
@ -105,8 +105,8 @@ namespace lib {
|
||||||
pointer address(reference r) const { return par_.address(r); }
|
pointer address(reference r) const { return par_.address(r); }
|
||||||
const_pointer address(const_reference cr) const { return par_.address(cr); }
|
const_pointer address(const_reference cr) const { return par_.address(cr); }
|
||||||
pointer allocate(size_type n, const void *p=0){ return par_.allocate(n,p); }
|
pointer allocate(size_type n, const void *p=0){ return par_.allocate(n,p); }
|
||||||
void deallocate(pointer p, size_type n) { return par_.deallocate(p,n); }
|
void deallocate(pointer p, size_type n) { par_.deallocate(p,n); }
|
||||||
void destroy(pointer p) { return par_.destroy(p); }
|
void destroy(pointer p) { par_.destroy(p); }
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -173,11 +173,21 @@ namespace lib {
|
||||||
return *obj;
|
return *obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TY&
|
||||||
|
create (TY const& o) ///< place new content object using copy ctor
|
||||||
|
{
|
||||||
|
ASSERT (!created_);
|
||||||
|
TY * obj = new(content_) TY(o);
|
||||||
|
++created_;
|
||||||
|
return *obj;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
clear ()
|
clear ()
|
||||||
{
|
{
|
||||||
if (created_)
|
if (created_)
|
||||||
get()->~TY();
|
get()->~TY();
|
||||||
|
created_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
302
src/lib/simple-allocator.hpp
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
/*
|
||||||
|
SIMPLE-ALLOCATOR.hpp - frontend for plain explicit allocations
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file simple-allocator.hpp
|
||||||
|
** Frontend and marker interface for allocating small objects explicitly.
|
||||||
|
** Contrary to the TypedAllocationManager, the SimpleAllocator doesn't provide any
|
||||||
|
** ref-counting or tracking facilities, nor does he support bulk de-allocation.
|
||||||
|
** Each object needs to be allocated and released by explicit call.
|
||||||
|
** The advantage over using std::allocator directly is the shortcut for (placement) construction,
|
||||||
|
** and -- of course -- the ability to exchange the memory model at one central location.
|
||||||
|
**
|
||||||
|
** SimpleAllocator instances will be defined for a specific collection of types; for each of those
|
||||||
|
** types, there will be an embedded dedicated custom allocator (currently as of 9/2011, just
|
||||||
|
** implemented as std::allocator<TY>). Objects of these preconfigured types can be constructed
|
||||||
|
** and destroyed through this allocator instance. Each call needs to be done explicitly, with
|
||||||
|
** the precise, concrete type to be created or destroyed. This is especially important for
|
||||||
|
** the releasing of objects: there is \em no support for any kind of virtual destruction.
|
||||||
|
**
|
||||||
|
** @see engine::BufferMetadata
|
||||||
|
** @see TypedAllocationManager (another more elaborate custom allocation scheme)
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIB_SIMPLE_ALLOCATOR_H
|
||||||
|
#define LIB_SIMPLE_ALLOCATOR_H
|
||||||
|
|
||||||
|
//#include "pre.hpp"
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "lib/meta/generator.hpp"
|
||||||
|
#include "lib/meta/typelist-util.hpp"
|
||||||
|
#include "lib/format.hpp"
|
||||||
|
#include "lib/typed-counter.hpp"
|
||||||
|
#include "include/logging.h"
|
||||||
|
|
||||||
|
#include <boost/static_assert.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace lib {
|
||||||
|
|
||||||
|
using lumiera::typelist::Types;
|
||||||
|
using lumiera::typelist::IsInList;
|
||||||
|
using lumiera::typelist::InstantiateForEach;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === Policies for simple custom allocator === */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy: use just plain heap allocations
|
||||||
|
*/
|
||||||
|
template<typename TY>
|
||||||
|
class CustomAllocator
|
||||||
|
: public std::allocator<TY>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy: maintain explicit per type instance count
|
||||||
|
* @note this imposes additional locking
|
||||||
|
*/
|
||||||
|
struct UseInstantiationCounting
|
||||||
|
{
|
||||||
|
template<class XX>
|
||||||
|
size_t
|
||||||
|
allocationCount() const
|
||||||
|
{
|
||||||
|
return allocCnt_.get<XX>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class XX>
|
||||||
|
void
|
||||||
|
incrementCount()
|
||||||
|
{
|
||||||
|
allocCnt_.inc<XX>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class XX>
|
||||||
|
void
|
||||||
|
decrementCount()
|
||||||
|
{
|
||||||
|
allocCnt_.dec<XX>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
lib::TypedCounter allocCnt_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy: no additional instantiation accounting
|
||||||
|
*/
|
||||||
|
struct NoInstantiationCount
|
||||||
|
{
|
||||||
|
template<class XX> size_t allocationCount() const { return 0; }
|
||||||
|
template<class XX> void incrementCount() { /* NOP */ }
|
||||||
|
template<class XX> void decrementCount() { /* NOP */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === Allocator frontend === */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frontend for explicit allocations, using a custom allocator.
|
||||||
|
* This template is to be instantiated for the collection of types
|
||||||
|
* later to be allocated through this custom memory manager/model.
|
||||||
|
* It provides convenience shortcuts for placement-construction
|
||||||
|
* and releasing of target objects.
|
||||||
|
*
|
||||||
|
* @todo currently (as of 8/09) the low-level pooled allocator
|
||||||
|
* isn't implemented; instead we do just heap allocations.
|
||||||
|
* ////////////////////////////////////////////////////////////////////////////////////////////Ticket #835
|
||||||
|
*/
|
||||||
|
template<typename TYPES
|
||||||
|
,class COUNTER = NoInstantiationCount ///< Policy: support instance accounting?
|
||||||
|
>
|
||||||
|
class SimpleAllocator
|
||||||
|
: InstantiateForEach< typename TYPES::List // for each of those types...
|
||||||
|
, CustomAllocator // ...mix in the custom allocator
|
||||||
|
>
|
||||||
|
, COUNTER
|
||||||
|
{
|
||||||
|
|
||||||
|
/** forward plain memory allocation */
|
||||||
|
template<class XX>
|
||||||
|
XX *
|
||||||
|
allocateSlot ()
|
||||||
|
{
|
||||||
|
TRACE (memory, "allocate %s", util::tyStr<XX>().c_str());
|
||||||
|
XX * newStorage = CustomAllocator<XX>::allocate (1);
|
||||||
|
COUNTER::template incrementCount<XX>();
|
||||||
|
return newStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class XX>
|
||||||
|
void
|
||||||
|
releaseSlot (XX* entry)
|
||||||
|
{
|
||||||
|
TRACE (memory, "release %s", util::tyStr<XX>().c_str());
|
||||||
|
CustomAllocator<XX>::deallocate (entry, 1);
|
||||||
|
COUNTER::template decrementCount<XX>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class XX>
|
||||||
|
void
|
||||||
|
___assertSupportedType()
|
||||||
|
{
|
||||||
|
typedef typename TYPES::List PreconfiguredTypes;
|
||||||
|
typedef IsInList<XX, PreconfiguredTypes> IsSupportedType;
|
||||||
|
|
||||||
|
BOOST_STATIC_ASSERT (IsSupportedType::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public: /* ==== build objects with managed allocation ==== */
|
||||||
|
|
||||||
|
#define _EXCEPTION_SAFE_INVOKE(_CTOR_) \
|
||||||
|
\
|
||||||
|
___assertSupportedType<XX>(); \
|
||||||
|
XX* storage = allocateSlot<XX>(); \
|
||||||
|
try \
|
||||||
|
{ \
|
||||||
|
return (new(storage) _CTOR_ ); \
|
||||||
|
} \
|
||||||
|
catch(...) \
|
||||||
|
{ \
|
||||||
|
releaseSlot<XX>(storage); \
|
||||||
|
throw; \
|
||||||
|
}
|
||||||
|
|
||||||
|
template< class XX>
|
||||||
|
XX* //_____________________
|
||||||
|
create () ///< invoke default ctor
|
||||||
|
{
|
||||||
|
_EXCEPTION_SAFE_INVOKE ( XX() )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template< class XX, typename P1>
|
||||||
|
XX* //___________________
|
||||||
|
create (P1& p1) ///< invoke 1-arg ctor
|
||||||
|
{
|
||||||
|
_EXCEPTION_SAFE_INVOKE ( XX (p1) )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template< class XX
|
||||||
|
, typename P1
|
||||||
|
, typename P2
|
||||||
|
>
|
||||||
|
XX* //___________________
|
||||||
|
create (P1& p1, P2& p2) ///< invoke 2-arg ctor
|
||||||
|
{
|
||||||
|
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2) )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template< class XX
|
||||||
|
, typename P1
|
||||||
|
, typename P2
|
||||||
|
, typename P3
|
||||||
|
>
|
||||||
|
XX* //___________________
|
||||||
|
create (P1& p1, P2& p2, P3& p3) ///< invoke 3-arg ctor
|
||||||
|
{
|
||||||
|
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3) )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template< class XX
|
||||||
|
, typename P1
|
||||||
|
, typename P2
|
||||||
|
, typename P3
|
||||||
|
, typename P4
|
||||||
|
>
|
||||||
|
XX* //___________________
|
||||||
|
create (P1& p1, P2& p2, P3& p3, P4& p4) ///< invoke 4-arg ctor
|
||||||
|
{
|
||||||
|
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4) )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template< class XX
|
||||||
|
, typename P1
|
||||||
|
, typename P2
|
||||||
|
, typename P3
|
||||||
|
, typename P4
|
||||||
|
, typename P5
|
||||||
|
>
|
||||||
|
XX* //___________________
|
||||||
|
create (P1& p1, P2& p2, P3& p3, P4& p4, P5& p5) ///< invoke 5-arg ctor
|
||||||
|
{
|
||||||
|
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4,p5) )
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef _EXCEPTION_SAFE_INVOKE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class XX>
|
||||||
|
void
|
||||||
|
destroy (XX* entry)
|
||||||
|
{
|
||||||
|
if (!entry) return;
|
||||||
|
___assertSupportedType<XX>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
entry->~XX();
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
lumiera_err errorID = lumiera_error();
|
||||||
|
WARN (common_dbg, "dtor of %s failed: %s", util::tyStr(entry).c_str()
|
||||||
|
, errorID );
|
||||||
|
}
|
||||||
|
releaseSlot<XX> (entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** diagnostics */
|
||||||
|
template<class XX>
|
||||||
|
size_t
|
||||||
|
numSlots() const
|
||||||
|
{
|
||||||
|
return COUNTER::template allocationCount<XX>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace lib
|
||||||
|
#endif
|
||||||
|
|
@ -110,7 +110,12 @@ namespace lumiera {
|
||||||
|
|
||||||
class TypeTag ;
|
class TypeTag ;
|
||||||
|
|
||||||
/** placeholder definition for the contents of a data buffer */
|
/**
|
||||||
|
* placeholder type for the contents of a data buffer.
|
||||||
|
* The actual buffer will always be provided by a
|
||||||
|
* library implementation; throughout the engine,
|
||||||
|
* it's just hidden behind a DataBuffer pointer.
|
||||||
|
*/
|
||||||
struct DataBuffer { };
|
struct DataBuffer { };
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,9 @@ using boost::hash_combine;
|
||||||
|
|
||||||
namespace lib {
|
namespace lib {
|
||||||
|
|
||||||
|
const size_t STRING_MAX_RELEVANT = 1000;
|
||||||
|
|
||||||
|
|
||||||
/** equality on Symbol values is defined
|
/** equality on Symbol values is defined
|
||||||
* based on the content, not the address. */
|
* based on the content, not the address. */
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ namespace lib {
|
||||||
/** safety guard: maximum number of chars to process.
|
/** safety guard: maximum number of chars to process.
|
||||||
* For comparisons, hash calculations etc., when dealing
|
* For comparisons, hash calculations etc., when dealing
|
||||||
* with raw char ptrs (typically literal values) */
|
* with raw char ptrs (typically literal values) */
|
||||||
const size_t STRING_MAX_RELEVANT = 1000;
|
extern const size_t STRING_MAX_RELEVANT;
|
||||||
|
|
||||||
|
|
||||||
/* ===== to be picked up by ADL ===== */
|
/* ===== to be picked up by ADL ===== */
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
@ -141,6 +143,9 @@ namespace test {
|
||||||
REQUIRE( !isnil(groupID) );
|
REQUIRE( !isnil(groupID) );
|
||||||
TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
|
TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
|
||||||
|
|
||||||
|
// Seed random number generator
|
||||||
|
std::srand (std::time (NULL));
|
||||||
|
|
||||||
if (!testcases.getGroup(groupID))
|
if (!testcases.getGroup(groupID))
|
||||||
throw lumiera::error::Invalid ("empty testsuite");
|
throw lumiera::error::Invalid ("empty testsuite");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
203
src/lib/time/control-impl.hpp
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
CONTROL-IMPL.hpp - time::control implementation building blocks
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file control-impl.hpp
|
||||||
|
** Implementation building blocks for time modification and propagation.
|
||||||
|
** The time::Control element allows to impose modifications to a connected
|
||||||
|
** time value entity and at the same time publish the changes to registered
|
||||||
|
** listeners. Due to the various flavours of actual time value entities, this
|
||||||
|
** is a complex undertaking, which is implemented here based on policies and
|
||||||
|
** template metaprogramming. This header/include defines two building blocks:
|
||||||
|
** - the actual Mutator to apply the changes to the target entity
|
||||||
|
** - a Propagator to register listeners and forward the changes.
|
||||||
|
**
|
||||||
|
** \par implementation technique
|
||||||
|
**
|
||||||
|
** The Mutator uses functor objects to encapsulate the actual modification
|
||||||
|
** operations. When attaching to a target time entity to be manipulated, these
|
||||||
|
** functor objects will be configured by binding them to the appropriate
|
||||||
|
** implementation function. And picking this actual implementation is done
|
||||||
|
** through a time::mutation::Policy element, using the concrete time entity
|
||||||
|
** types as template parameter. Thus, the actual implementation to be used
|
||||||
|
** is determined by the compiler, through the template specialisations
|
||||||
|
** contained in control-policy.hpp
|
||||||
|
**
|
||||||
|
** @note the header control-policy.hpp with the template specialisations
|
||||||
|
** is included way down, after the class definitions. This is done
|
||||||
|
** so for sake of readability
|
||||||
|
**
|
||||||
|
** @see TimeControl_test
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIB_TIME_CONTROL_IMPL_H
|
||||||
|
#define LIB_TIME_CONTROL_IMPL_H
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "lib/time/mutation.hpp"
|
||||||
|
#include "lib/time/timevalue.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace lib {
|
||||||
|
namespace time {
|
||||||
|
namespace mutation {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation building block: impose changes to a Time element.
|
||||||
|
* The purpose of the Mutator is to attach a target time entity,
|
||||||
|
* which then will be subject to any received value changes,
|
||||||
|
* offsets and grid nudging. The actual attachment is to be
|
||||||
|
* performed in a subclass, by using the Mutation interface.
|
||||||
|
* When attaching to a target, the Mutator will be outfitted
|
||||||
|
* with a set of suitable functors, incorporating the specific
|
||||||
|
* behaviour for the concrete combination of input changes
|
||||||
|
* ("source values") and target object type. This works by
|
||||||
|
* binding to the appropriate implementation functionality,
|
||||||
|
* guided by a templated policy class. After installing
|
||||||
|
* these functors, these decisions remains opaque and
|
||||||
|
* encapsulated within the functor objects, so the
|
||||||
|
* mutator object doesn't need to carry this
|
||||||
|
* type information on the interface
|
||||||
|
*/
|
||||||
|
template<class TI>
|
||||||
|
class Mutator
|
||||||
|
: public Mutation
|
||||||
|
{
|
||||||
|
typedef function<TI(TI const&)> ValueSetter;
|
||||||
|
typedef function<TI(Offset const&)> Ofsetter;
|
||||||
|
typedef function<TI(int)> Nudger;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutable ValueSetter setVal_;
|
||||||
|
mutable Ofsetter offset_;
|
||||||
|
mutable Nudger nudge_;
|
||||||
|
|
||||||
|
void
|
||||||
|
ensure_isArmed() const
|
||||||
|
{
|
||||||
|
if (!setVal_)
|
||||||
|
throw error::State("feeding time/value change "
|
||||||
|
"while not (yet) connected to any target to change"
|
||||||
|
,error::LUMIERA_ERROR_UNCONNECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class TAR>
|
||||||
|
void bind_to (TAR& target) const;
|
||||||
|
|
||||||
|
void unbind();
|
||||||
|
|
||||||
|
// using default construction and copy
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation building block: propagate changes to listeners.
|
||||||
|
* The Propagator manages a set of callback signals, allowing to
|
||||||
|
* propagate notifications for changed Time values.
|
||||||
|
*
|
||||||
|
* There are no specific requirements on the acceptable listeners,
|
||||||
|
* besides exposing a function-call operator to feed the changed
|
||||||
|
* time value to. Both Mutator and Propagator employ one primary
|
||||||
|
* template parameter, which is the type of the time values
|
||||||
|
* to be fed in and propagated.
|
||||||
|
*/
|
||||||
|
template<class TI>
|
||||||
|
class Propagator
|
||||||
|
{
|
||||||
|
typedef function<void(TI const&)> ChangeSignal;
|
||||||
|
typedef std::vector<ChangeSignal> ListenerList;
|
||||||
|
|
||||||
|
ListenerList listeners_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** install notification receiver */
|
||||||
|
template<class SIG>
|
||||||
|
void
|
||||||
|
attach (SIG const& toNotify)
|
||||||
|
{
|
||||||
|
ChangeSignal newListener (ref(toNotify));
|
||||||
|
listeners_.push_back (newListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** disconnect any observers */
|
||||||
|
void
|
||||||
|
disconnnect()
|
||||||
|
{
|
||||||
|
listeners_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** publish a change */
|
||||||
|
TI
|
||||||
|
operator() (TI const& changedVal) const
|
||||||
|
{
|
||||||
|
typedef typename ListenerList::const_iterator Iter;
|
||||||
|
Iter p = listeners_.begin();
|
||||||
|
Iter e = listeners_.end();
|
||||||
|
|
||||||
|
for ( ; p!=e; ++p )
|
||||||
|
(*p) (changedVal);
|
||||||
|
return changedVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// using default construction and copy
|
||||||
|
};
|
||||||
|
|
||||||
|
}}} // lib::time::mutation
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Definition of actual operations ===== */
|
||||||
|
#include "lib/time/control-policy.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
template<class TAR>
|
||||||
|
void
|
||||||
|
lib::time::mutation::Mutator<TI>::bind_to (TAR& target) const
|
||||||
|
{
|
||||||
|
using lib::time::mutation::Policy;
|
||||||
|
|
||||||
|
setVal_ = Policy<TI,TI, TAR>::buildChangeHandler (target);
|
||||||
|
offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target);
|
||||||
|
nudge_ = Policy<TI,int, TAR>::buildChangeHandler (target);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
void
|
||||||
|
lib::time::mutation::Mutator<TI>::unbind()
|
||||||
|
{
|
||||||
|
setVal_ = ValueSetter();
|
||||||
|
offset_ = Ofsetter();
|
||||||
|
nudge_ = Nudger();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
400
src/lib/time/control-policy.hpp
Normal file
|
|
@ -0,0 +1,400 @@
|
||||||
|
/*
|
||||||
|
CONTROL-POLICY.hpp - detail definition of actual time changing functionality
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file control-policy.hpp
|
||||||
|
** Definition of special cases when imposing a change onto concrete time values.
|
||||||
|
** The time::Control element allows to impose modifications to a connected
|
||||||
|
** time value entity and at the same time publish the changes to registered
|
||||||
|
** listeners. Due to the various flavours of actual time value entities, this
|
||||||
|
** is a complex undertaking, which is implemented here based on policies and
|
||||||
|
** template metaprogramming.
|
||||||
|
**
|
||||||
|
** The header control-impl.hpp defines the building blocks for time::Control
|
||||||
|
** and then includes this header here to get the concrete template specialisations
|
||||||
|
** for time::mutation::Policy. This policy class is templated by time entity types
|
||||||
|
** - for \c TI, the \em nominal value type used on the time::Control interface
|
||||||
|
** - for \c SRC, the actual type of values to impose as \em change
|
||||||
|
** - for \c TAR, the target time value's type, receiving those changes.
|
||||||
|
**
|
||||||
|
** \par mutating a time value entity
|
||||||
|
**
|
||||||
|
** Actually imposing a change to the attached time value entity involves several
|
||||||
|
** steps. Each of these steps might be adapted specifically, in accordance to
|
||||||
|
** the concrete time value types involved.
|
||||||
|
** - TimeValue, Time
|
||||||
|
** - Offset
|
||||||
|
** - Duration
|
||||||
|
** - TimeSpan
|
||||||
|
** - QuTime (grid aligned time value)
|
||||||
|
** - QuTimeSpan (planned as of 6/2011)
|
||||||
|
**
|
||||||
|
** Moreover, the combination of types needs to be taken into account. For example,
|
||||||
|
** it doesn't make sense to apply a Duration value as change to a TimeValue, which
|
||||||
|
** has no duration (temporal extension). While a TimeSpan might receive a Duration
|
||||||
|
** change, but behaves differently when imposing a Time to manipulate the starting
|
||||||
|
** point of the time interval given by the TimeSpan.
|
||||||
|
**
|
||||||
|
** Incoming changes might be of any of the aforementioned types, and in addition,
|
||||||
|
** we might receive \em nudging, which means to increment or decrement the target
|
||||||
|
** time value in discrete steps. After maybe adapting these incoming change values,
|
||||||
|
** they may be actually \em imposed to the target. In all cases, this is delegated
|
||||||
|
** to the time::Mutation base class, which is declared fried to TimeValue and thus
|
||||||
|
** has the exceptional ability to manipulate time values, which otherwise are defined
|
||||||
|
** to be immutable. Additionally, these protected functions in the time::Mutation
|
||||||
|
** baseclass also know how to handle \em nudge values, either by using the native
|
||||||
|
** (embedded) time grid of a quantised time value, or by falling back to a standard
|
||||||
|
** nudging grid, defined in the session context (TODO as of 6/2011). //////////////////////TICKET #810
|
||||||
|
**
|
||||||
|
** After (maybe) imposing a change to the target, the change \em notification value
|
||||||
|
** needs to be built. This is the time value entity to be forwarded to registered
|
||||||
|
** listeners. This notification value has to be given as the type \c TI, in accordance
|
||||||
|
** to the \c time::Control<TI> frontend definition used in the concrete usage situation.
|
||||||
|
** As this type \c TI might be different to the actual target type, and again different
|
||||||
|
** to the type of the change handed in, in some cases this involves a second conversion
|
||||||
|
** step, to represent the current state of the target \c TAR in terms of the interface
|
||||||
|
** type \c TI.
|
||||||
|
**
|
||||||
|
** \par changing quantised (grid aligned) time entities
|
||||||
|
**
|
||||||
|
** The time::Control element includes the capability to handle grid aligned time values,
|
||||||
|
** both as target and as change/notification value. This ability is compiled in conditionally,
|
||||||
|
** as including mutation.hpp causes several additional includes, which isn't desirable when
|
||||||
|
** it comes just to changing plain time values. Thus, to get these additional specialisations,
|
||||||
|
** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically
|
||||||
|
** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
|
||||||
|
**
|
||||||
|
** As a special convention, any \em quantised (grid aligned) types involved in these
|
||||||
|
** time changes will be \em materialised, whenever a type conversion happens. Generally
|
||||||
|
** speaking, a quantised time value contains an (opaque) raw time value, plus a reference
|
||||||
|
** to a time grid definition to apply. In this context \em materialising means actually
|
||||||
|
** to apply this time grid to yield a grid aligned value. Thus, when using a quantised
|
||||||
|
** value to impose as change (or to receive a change), its grid aligning nature
|
||||||
|
** becomes effective, by applying the \em current definition of the grid to
|
||||||
|
** create a fixed (materialised) time value, aligned to that current grid.
|
||||||
|
**
|
||||||
|
** @todo 6/2011 include all the special cases for QuTimeSpan ////////////////////TICKET #760
|
||||||
|
**
|
||||||
|
** @see TimeControl_test
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIB_TIME_CONTROL_POLICY_H
|
||||||
|
#define LIB_TIME_CONTROL_POLICY_H
|
||||||
|
|
||||||
|
#include "lib/meta/util.hpp"
|
||||||
|
#include "lib/time/mutation.hpp"
|
||||||
|
#include "lib/time/timevalue.hpp"
|
||||||
|
|
||||||
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
#include <tr1/functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace lib {
|
||||||
|
namespace time {
|
||||||
|
|
||||||
|
namespace mutation {
|
||||||
|
|
||||||
|
using boost::disable_if;
|
||||||
|
using lumiera::typelist::is_sameType;
|
||||||
|
using std::tr1::placeholders::_1;
|
||||||
|
using std::tr1::function;
|
||||||
|
using std::tr1::bind;
|
||||||
|
using std::tr1::ref;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace { // metaprogramming helpers to pick a suitable implementation branch...
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline bool
|
||||||
|
isDuration()
|
||||||
|
{
|
||||||
|
return is_sameType<T,Duration>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline bool
|
||||||
|
isTimeSpan()
|
||||||
|
{
|
||||||
|
return is_sameType<T,TimeSpan>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T const&
|
||||||
|
maybeMaterialise (T const& non_grid_aligned_TimeValue)
|
||||||
|
{
|
||||||
|
return non_grid_aligned_TimeValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LIB_TIME_TIMEQUQNT_H
|
||||||
|
inline QuTime
|
||||||
|
maybeMaterialise (QuTime const& alignedTime)
|
||||||
|
{
|
||||||
|
PQuant const& grid(alignedTime);
|
||||||
|
return QuTime(grid->materialise(alignedTime), grid);
|
||||||
|
}
|
||||||
|
#endif //--quantised-time-support
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation policy: how to build a new
|
||||||
|
* notification value of type \c TI, given a
|
||||||
|
* target time value entity of type \c TAR
|
||||||
|
*/
|
||||||
|
template<class TI, class TAR>
|
||||||
|
struct Builder
|
||||||
|
{
|
||||||
|
static TI
|
||||||
|
buildChangedValue (TAR const& target)
|
||||||
|
{
|
||||||
|
return TI(target);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<class TAR>
|
||||||
|
struct Builder<TimeSpan, TAR>
|
||||||
|
{
|
||||||
|
static TimeSpan
|
||||||
|
buildChangedValue (TAR const& target)
|
||||||
|
{
|
||||||
|
return TimeSpan (target, Duration::NIL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct Builder<TimeSpan, Duration>
|
||||||
|
{
|
||||||
|
static TimeSpan
|
||||||
|
buildChangedValue (Duration const& targetDuration)
|
||||||
|
{
|
||||||
|
return TimeSpan (Time::ZERO, targetDuration);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct Builder<TimeSpan, TimeSpan>
|
||||||
|
{
|
||||||
|
static TimeSpan
|
||||||
|
buildChangedValue (TimeSpan const& target)
|
||||||
|
{
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#ifdef LIB_TIME_TIMEQUQNT_H
|
||||||
|
template<class TAR>
|
||||||
|
struct Builder<QuTime, TAR>
|
||||||
|
{
|
||||||
|
static QuTime
|
||||||
|
buildChangedValue (TAR const& target)
|
||||||
|
{
|
||||||
|
return QuTime (target
|
||||||
|
,getDefaultGridFallback() //////////////////TICKET #810
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct Builder<QuTime, QuTime>
|
||||||
|
{
|
||||||
|
static QuTime
|
||||||
|
buildChangedValue (QuTime const& target)
|
||||||
|
{
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif //--quantised-time-support
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy to tie the various detail policies together
|
||||||
|
* for providing actual value change operations.
|
||||||
|
* The standard case uses the (inherited) time::Mutation
|
||||||
|
* base implementation to impose a new value onto the
|
||||||
|
* target entity and then uses the Builder policy to
|
||||||
|
* create a notification value reflecting this change.
|
||||||
|
*/
|
||||||
|
template<class TI, class TAR>
|
||||||
|
struct Link
|
||||||
|
: Mutation
|
||||||
|
, Builder<TI,TAR>
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class SRC>
|
||||||
|
static TI
|
||||||
|
processValueChange (TAR& target, SRC const& change) ///< standard case: plain value change
|
||||||
|
{
|
||||||
|
imposeChange (target, maybeMaterialise(change));
|
||||||
|
return buildChangedValue (maybeMaterialise(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
static TI
|
||||||
|
useLengthAsChange (TAR& target, TimeSpan const& change)
|
||||||
|
{
|
||||||
|
return processValueChange(target, change.duration());
|
||||||
|
}
|
||||||
|
|
||||||
|
static TI
|
||||||
|
mutateLength (TimeSpan& target, Duration const& change)
|
||||||
|
{
|
||||||
|
imposeChange (target.duration(), change);
|
||||||
|
return Builder<TI,TimeSpan>::buildChangedValue(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TimeSpan
|
||||||
|
mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
|
||||||
|
{
|
||||||
|
imposeChange (target.duration(), change.duration());
|
||||||
|
imposeChange (target,change.start());
|
||||||
|
return Builder<TimeSpan,TimeSpan>::buildChangedValue(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TI
|
||||||
|
dontChange (TAR& target) ///< @note: not touching the target
|
||||||
|
{
|
||||||
|
return buildChangedValue(target);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy how to impose changes onto a connected target time value entity
|
||||||
|
* This policy will be parametrised with the concrete time entity types
|
||||||
|
* involved in the usage situation of time::Control. The purpose of the
|
||||||
|
* policy is to \em bind a functor object to the concrete implementation
|
||||||
|
* of the value change applicable for this combination of types.
|
||||||
|
* This functor will then be stored within time::Control and
|
||||||
|
* invoked for each actual value change.
|
||||||
|
* @param TI the nominal (interface) type of the change, propagated to listeners
|
||||||
|
* @param SRC the actual type of the change to be imposed
|
||||||
|
* @param TAR the actual type of the target entity to receive the changes
|
||||||
|
* @note typically either SRC is identical to TI, or it is an
|
||||||
|
* time::Offset, or an int for \em nudging the target
|
||||||
|
*/
|
||||||
|
template<class TI, class SRC, class TAR>
|
||||||
|
struct Policy
|
||||||
|
{
|
||||||
|
static function<TI(SRC const&)>
|
||||||
|
buildChangeHandler (TAR& target)
|
||||||
|
{
|
||||||
|
return bind (Link<TI,TAR>::template processValueChange<SRC>, ref(target), _1 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// special treatment of Durations as target------------------------------------
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<class T>
|
||||||
|
struct canMutateDuration
|
||||||
|
{
|
||||||
|
static const bool value = is_sameType<T,Duration>::value
|
||||||
|
|| is_sameType<T,Offset>::value
|
||||||
|
|| is_sameType<T,int>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct canReceiveDuration
|
||||||
|
{
|
||||||
|
static const bool value = is_sameType<T,Duration>::value
|
||||||
|
|| is_sameType<T,TimeSpan>::value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* special case: a Duration target value can't be changed by plain time values.
|
||||||
|
* This specialisation is \em not used (\c disable_if ) when the given change (SRC)
|
||||||
|
* is applicable to a Duration in a sensible way. We either define explicit
|
||||||
|
* specialisations (for TimeSpan) or fall back to the default in these cases.
|
||||||
|
*/
|
||||||
|
template<class TI, class SRC>
|
||||||
|
struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>,
|
||||||
|
Duration>::type>
|
||||||
|
{
|
||||||
|
static function<TI(SRC const&)>
|
||||||
|
buildChangeHandler (Duration& target)
|
||||||
|
{
|
||||||
|
return bind (Link<TI,Duration>::dontChange, ref(target) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* special case: a Duration change value can't be imposed to a plain time value.
|
||||||
|
* In these cases, we even propagate a Duration::ZERO to the listeners.
|
||||||
|
* As above, there are exceptions to this behaviour, where a Duration change
|
||||||
|
* can sensibly be applied.
|
||||||
|
*/
|
||||||
|
template<class TAR>
|
||||||
|
struct Policy<Duration, typename disable_if< canReceiveDuration<TAR>,
|
||||||
|
Duration>::type, TAR>
|
||||||
|
{
|
||||||
|
static function<Duration(Duration const&)>
|
||||||
|
buildChangeHandler (TAR&)
|
||||||
|
{
|
||||||
|
return bind ( ignore_change_and_return_Zero );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Duration
|
||||||
|
ignore_change_and_return_Zero()
|
||||||
|
{
|
||||||
|
return Duration::NIL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
struct Policy<TI,TimeSpan,Duration>
|
||||||
|
{
|
||||||
|
static function<TI(TimeSpan const&)>
|
||||||
|
buildChangeHandler (Duration& target)
|
||||||
|
{
|
||||||
|
return bind (Link<TI,Duration>::useLengthAsChange, ref(target), _1 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// special treatment for TimeSpan values---------------------------------------
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
struct Policy<TI,Duration,TimeSpan>
|
||||||
|
{
|
||||||
|
static function<TI(Duration const&)>
|
||||||
|
buildChangeHandler (TimeSpan& target)
|
||||||
|
{
|
||||||
|
return bind (Link<TI,TimeSpan>::mutateLength, ref(target), _1 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Policy<TimeSpan,TimeSpan,TimeSpan>
|
||||||
|
{
|
||||||
|
static function<TimeSpan(TimeSpan const&)>
|
||||||
|
buildChangeHandler (TimeSpan& target)
|
||||||
|
{
|
||||||
|
return bind (Link<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}}} // namespace lib::time::mutation
|
||||||
|
#endif
|
||||||
256
src/lib/time/control.hpp
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
/*
|
||||||
|
CONTROL.hpp - a life time control for feedback and mutation
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file control.hpp
|
||||||
|
** Manipulating and monitoring time entities with life changes.
|
||||||
|
** This is an control- and callback element to handle any kind of "running"
|
||||||
|
** time entities. This element is to be provided by the client and then attached
|
||||||
|
** to the target time entity as a time::Mutation. Internally, a life connection to
|
||||||
|
** the target is built, allowing both to
|
||||||
|
** - to manipulate the target by invoking the function operator
|
||||||
|
** - to receive change notifications by installing a callback functor.
|
||||||
|
**
|
||||||
|
** The actual type of the changes and modifications is specified as template parameter;
|
||||||
|
** when later attached to some time entity as a Mutation, the actual changes to be performed
|
||||||
|
** depend both on this change type and the type of the target time entity (double dispatch).
|
||||||
|
** The behaviour is similar to applying a static time::Mutation
|
||||||
|
**
|
||||||
|
** \par relevance
|
||||||
|
** This control element is intended to be used for all kinds of editing and monitoring
|
||||||
|
** of time-like entities -- be it the running time display in a GUI widget, a ruler marker
|
||||||
|
** which can be dragged, a modifiable selection or the animated playhead cursor.
|
||||||
|
**
|
||||||
|
** \par usage scenarios
|
||||||
|
** The time::Control element provides mediating functionality, but doesn't assume or provide
|
||||||
|
** anything special regarding the usage pattern or the lifecycle, beyond the ability to
|
||||||
|
** attach listeners, attach to a (different) target and to detach from all connections.
|
||||||
|
** Especially, no assumptions are made about which side is the server or the client
|
||||||
|
** and who owns the time::Control element.
|
||||||
|
**
|
||||||
|
** Thus an interface might accept a time::Control element \em reference (e.g. the
|
||||||
|
** lumiera::Play::Controller uses this pattern) -- meaning that the client owns the
|
||||||
|
** Control element and might attach listeners, while the implementation (server side)
|
||||||
|
** will attach the Control to mutate an time value entity otherwise not disclosed
|
||||||
|
** (e.g. the playhead position of the playback process). Of course, in this case
|
||||||
|
** the client is responsible for keeping the Control element and all listeners
|
||||||
|
** alive, and to invoke Control#disconnect prior to destroying the element.
|
||||||
|
**
|
||||||
|
** Of course, the reversed usage situation would be possible as well: an interface
|
||||||
|
** exposing a time::Control, thus allowing to attach target and listeners, while the
|
||||||
|
** actual changes will originate somewhere within the service implementation.
|
||||||
|
**
|
||||||
|
** Another usage pattern would be to expose a time::Control \c const&, allowing only to
|
||||||
|
** impose changes, but not to change the target or listener attachments. To the contrary,
|
||||||
|
** when exposing only a time::Mutation \c const& through an interface allows only to
|
||||||
|
** attach new target elements, but not to change listeners or feed any value changes.
|
||||||
|
**
|
||||||
|
** Using time::Control as an implementation building block and just exposing the
|
||||||
|
** change (function) operators or the listener attachment through an forwarding sub
|
||||||
|
** interface is another option.
|
||||||
|
**
|
||||||
|
** @note time::Control is default constructible and freely copyable.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** \par changing quantised (grid aligned) time entities
|
||||||
|
**
|
||||||
|
** The time::Control element includes the functionality to handle grid aligned time values,
|
||||||
|
** both as target and as change/notification value. This ability is compiled in conditionally,
|
||||||
|
** as including mutation.hpp causes several additional includes, which isn't desirable when
|
||||||
|
** it comes just to changing plain time values. Thus, to get these additional specialisations,
|
||||||
|
** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically
|
||||||
|
** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
|
||||||
|
**
|
||||||
|
** \par implementation notes
|
||||||
|
** - the validity of a given combination of change and target is checked immediately,
|
||||||
|
** when connecting to the target. Depending on the situation, the actual changes later
|
||||||
|
** are subject to specific treatment (e.g. frame quantisation)
|
||||||
|
** - by default time::Control is <b>not threadsafe</b>. But, as each change is basically
|
||||||
|
** processed within its own call context (function invocation), parallelism is only
|
||||||
|
** a concern with respect to the value finally visible within the target.
|
||||||
|
** - the change notification is processed right away, after applying the change to the
|
||||||
|
** target; of course there is a race between applying the value and building the
|
||||||
|
** response value passed on as notification. In all cases, the effective change
|
||||||
|
** notification value is built from the state of the target after applying
|
||||||
|
** the change, which might or might not reflect the change value passed in.
|
||||||
|
**
|
||||||
|
** @todo include support for QuTimeSpan values ////////////////////TICKET #760
|
||||||
|
**
|
||||||
|
** @see TimeControl_test
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIB_TIME_CONTROL_H
|
||||||
|
#define LIB_TIME_CONTROL_H
|
||||||
|
|
||||||
|
#include "lib/time/mutation.hpp"
|
||||||
|
#include "lib/time/timevalue.hpp"
|
||||||
|
#include "lib/time/control-impl.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace lib {
|
||||||
|
namespace time {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frontend/Interface: controller-element to retrieve
|
||||||
|
* and change running time values. time::Control is
|
||||||
|
* a mediator element, which can be attached to some
|
||||||
|
* time value entities as \em mutation, and at the
|
||||||
|
* same time allows to register listeners. When
|
||||||
|
* configured this way, \em changes may be fed
|
||||||
|
* to the function operator(s). These changes
|
||||||
|
* will be imposed to the connected target
|
||||||
|
* and the result propagated to the listeners.
|
||||||
|
*
|
||||||
|
* @see time::Mutation
|
||||||
|
* @see time::TimeSpan#accept(Mutation const&)
|
||||||
|
*/
|
||||||
|
template<class TI>
|
||||||
|
class Control
|
||||||
|
: public mutation::Mutator<TI>
|
||||||
|
{
|
||||||
|
mutation::Propagator<TI> notifyListeners_;
|
||||||
|
|
||||||
|
virtual void change (Duration&) const;
|
||||||
|
virtual void change (TimeSpan&) const;
|
||||||
|
virtual void change (QuTime&) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void operator() (TI const&) const;
|
||||||
|
void operator() (Offset const&) const;
|
||||||
|
void operator() (int) const;
|
||||||
|
|
||||||
|
|
||||||
|
/** install a callback functor to be invoked as notification
|
||||||
|
* for any changes imposed onto the observed time entity.
|
||||||
|
* @param toNotify object with \c operator()(TI const&) */
|
||||||
|
template<class SIG>
|
||||||
|
void connectChangeNotification (SIG const& toNotify);
|
||||||
|
|
||||||
|
/** disconnect from observed entity and
|
||||||
|
* cease any change notification */
|
||||||
|
void disconnnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === forward to implementation === */
|
||||||
|
|
||||||
|
/** impose a new value to the connected target.
|
||||||
|
* If applicable, the target will afterwards reflect
|
||||||
|
* that change, and listeners will be notified, passing
|
||||||
|
* the target's new state.
|
||||||
|
* @throw error::State if not connected to a target
|
||||||
|
* @note the actual change in the target also depends
|
||||||
|
* on the concrete target type and the type of
|
||||||
|
* the change. By default, the time value is
|
||||||
|
* changed; this may include grid alignment.
|
||||||
|
*/
|
||||||
|
template<class TI>
|
||||||
|
void
|
||||||
|
Control<TI>::operator () (TI const& newValue) const
|
||||||
|
{
|
||||||
|
this->ensure_isArmed();
|
||||||
|
notifyListeners_(
|
||||||
|
this->setVal_(newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** impose an offset to the connected target.
|
||||||
|
* If applicable, the target will be adjusted by the
|
||||||
|
* time offset, and listeners will be notified.
|
||||||
|
* @throw error::State if not connected to a target
|
||||||
|
*/
|
||||||
|
template<class TI>
|
||||||
|
void
|
||||||
|
Control<TI>::operator () (Offset const& adjustment) const
|
||||||
|
{
|
||||||
|
this->ensure_isArmed();
|
||||||
|
notifyListeners_(
|
||||||
|
this->offset_(adjustment));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** nudge the connected target by the given offset steps,
|
||||||
|
* using either the target's own grid (when quantised),
|
||||||
|
* or a 'natural' nudge grid
|
||||||
|
* @throw error::State if not connected to a target
|
||||||
|
*/
|
||||||
|
template<class TI>
|
||||||
|
void
|
||||||
|
Control<TI>::operator () (int offset_by_steps) const
|
||||||
|
{
|
||||||
|
this->ensure_isArmed();
|
||||||
|
notifyListeners_(
|
||||||
|
this->nudge_(offset_by_steps));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
void
|
||||||
|
Control<TI>::disconnnect()
|
||||||
|
{
|
||||||
|
notifyListeners_.disconnect();
|
||||||
|
this->unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
template<class SIG>
|
||||||
|
void
|
||||||
|
Control<TI>::connectChangeNotification (SIG const& toNotify)
|
||||||
|
{
|
||||||
|
if (this->offset_)
|
||||||
|
{ // we're already connected: thus propagate current value
|
||||||
|
TI currentValue = this->offset_(Offset::ZERO);
|
||||||
|
toNotify (currentValue);
|
||||||
|
}
|
||||||
|
notifyListeners_.attach (toNotify);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==== Implementation of the Mutation interface ==== */
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
void
|
||||||
|
Control<TI>::change (Duration& targetDuration) const
|
||||||
|
{
|
||||||
|
this->bind_to (targetDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
void
|
||||||
|
Control<TI>::change (TimeSpan& targetInterval) const
|
||||||
|
{
|
||||||
|
this->bind_to (targetInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TI>
|
||||||
|
void
|
||||||
|
Control<TI>::change (QuTime& targetQuTime) const
|
||||||
|
{
|
||||||
|
this->bind_to (targetQuTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
}} // lib::time
|
||||||
|
#endif
|
||||||
|
|
@ -41,6 +41,9 @@ namespace time {
|
||||||
const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) );
|
const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) );
|
||||||
const Time Time::ZERO;
|
const Time Time::ZERO;
|
||||||
|
|
||||||
|
const Offset Offset::ZERO (Time::ZERO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** convenience constructor to build an
|
/** convenience constructor to build an
|
||||||
* internal Lumiera Time value from the usual parts
|
* internal Lumiera Time value from the usual parts
|
||||||
|
|
@ -116,14 +119,21 @@ namespace time {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** duration of the given number of frames */
|
/** offset by the given number of frames. */
|
||||||
Duration::Duration (ulong count, FrameRate const& fps)
|
Offset::Offset (int64_t count, FrameRate const& fps)
|
||||||
: Offset(TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL)))
|
: TimeValue (count? (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
|
||||||
|
: _raw(Duration::NIL))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/** duration of the given number of frames.
|
||||||
|
* @note always positive; count used absolute */
|
||||||
|
Duration::Duration (int64_t count, FrameRate const& fps)
|
||||||
|
: TimeValue (count? lumiera_framecount_to_time (abs(count), fps) : _raw(Duration::NIL))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
||||||
/** constant to indicate "no duration" */
|
/** constant to indicate "no duration" */
|
||||||
const Duration Duration::NIL = Offset(TimeValue(0));
|
const Duration Duration::NIL (Time::ZERO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,17 +48,6 @@ namespace time {
|
||||||
Mutation::~Mutation() { } // emit VTable here....
|
Mutation::~Mutation() { } // emit VTable here....
|
||||||
|
|
||||||
|
|
||||||
/** @internal actually force a change
|
|
||||||
* into a target time entity to mutate.
|
|
||||||
* Mutation is declared fried to TimeValue
|
|
||||||
* and thus is allowed to influence the basic
|
|
||||||
* value stored in each time entity
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet)
|
|
||||||
{
|
|
||||||
target = valueToSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -157,14 +146,14 @@ namespace time {
|
||||||
virtual void
|
virtual void
|
||||||
change (Duration& target) const
|
change (Duration& target) const
|
||||||
{
|
{
|
||||||
imposeChange (target, TimeVar(target)+=adjustment_);
|
imposeChange (target, adjustment_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
change (TimeSpan& target) const
|
change (TimeSpan& target) const
|
||||||
{
|
{
|
||||||
imposeChange (target, TimeVar(target)+=adjustment_);
|
imposeChange (target, adjustment_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -173,7 +162,7 @@ namespace time {
|
||||||
virtual void
|
virtual void
|
||||||
change (QuTime& target) const
|
change (QuTime& target) const
|
||||||
{
|
{
|
||||||
imposeChange (target, TimeVar(target)+=adjustment_);
|
imposeChange (target, adjustment_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -205,7 +194,7 @@ namespace time {
|
||||||
/**
|
/**
|
||||||
* concrete time value mutation:
|
* concrete time value mutation:
|
||||||
* nudge target value by the given number of 'steps',
|
* nudge target value by the given number of 'steps',
|
||||||
* relative to either the given grid.
|
* relative to the given grid.
|
||||||
*/
|
*/
|
||||||
class NudgeMutation
|
class NudgeMutation
|
||||||
: public ImposeOffsetMutation
|
: public ImposeOffsetMutation
|
||||||
|
|
@ -234,6 +223,8 @@ namespace time {
|
||||||
* @note currently the natural grid is hard wired,
|
* @note currently the natural grid is hard wired,
|
||||||
* just interpreting the step parameter as
|
* just interpreting the step parameter as
|
||||||
* offset in seconds.
|
* offset in seconds.
|
||||||
|
* @see mutation#imposeChange (TimeValue, int)
|
||||||
|
* @see mutation#imposeChange (QuTime, int)
|
||||||
*/
|
*/
|
||||||
class NaturalNudgeMutation
|
class NaturalNudgeMutation
|
||||||
: public ClonableMutation
|
: public ClonableMutation
|
||||||
|
|
@ -243,31 +234,22 @@ namespace time {
|
||||||
virtual void
|
virtual void
|
||||||
change (Duration& target) const
|
change (Duration& target) const
|
||||||
{
|
{
|
||||||
imposeChange (target, TimeVar(target)+=Time(FSecs(steps_)));
|
imposeChange (target, steps_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
change (TimeSpan& target) const
|
change (TimeSpan& target) const
|
||||||
{
|
{
|
||||||
imposeChange (target, TimeVar(target)+=Time(FSecs(steps_)));
|
imposeChange (target, steps_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Special treatment: use the quantised time's own grid;
|
/** @note special treatment: use the quantised time's own grid */
|
||||||
* retrieve the corresponding grid point, offset it by the step-parameter,
|
|
||||||
* then retrieve the corresponding time from the quantised time's
|
|
||||||
* underlying quantiser (grid).
|
|
||||||
* @note when the #steps_ parameter is zero, what happens here effectively
|
|
||||||
* is the materialisation of the quantised target time, i.e. making
|
|
||||||
* the quantisation explicit and storing the resulting value. */
|
|
||||||
virtual void
|
virtual void
|
||||||
change (QuTime& target) const
|
change (QuTime& target) const
|
||||||
{
|
{
|
||||||
PQuant const& grid (target);
|
imposeChange (target, steps_);
|
||||||
int64_t originalGridPoint = grid->gridPoint(target);
|
|
||||||
int64_t adjustedGridPoint = originalGridPoint + steps_;
|
|
||||||
imposeChange (target, grid->timeOf (adjustedGridPoint));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,10 @@ namespace time {
|
||||||
static EncapsulatedMutation nudge (int adjustment, Symbol gridID);
|
static EncapsulatedMutation nudge (int adjustment, Symbol gridID);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void imposeChange (TimeValue&, TimeValue const&);
|
static TimeValue& imposeChange (TimeValue&, TimeValue const&);
|
||||||
|
static TimeValue& imposeChange (TimeValue&, Offset const&);
|
||||||
|
static TimeValue& imposeChange (TimeValue&, int);
|
||||||
|
static TimeValue& imposeChange (QuTime&, int);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -126,5 +129,53 @@ namespace time {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === implementing the actual changes === */
|
||||||
|
|
||||||
|
/** @internal actually force a change into a target time entity to mutate.
|
||||||
|
* Mutation is declared fried to TimeValue and thus is allowed to influence
|
||||||
|
* the basic value stored in each time entity
|
||||||
|
*/
|
||||||
|
inline TimeValue&
|
||||||
|
Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet)
|
||||||
|
{
|
||||||
|
return target = valueToSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal variation to mutate a target time value by applying an offset */
|
||||||
|
inline TimeValue&
|
||||||
|
Mutation::imposeChange (TimeValue& target, Offset const& offset)
|
||||||
|
{
|
||||||
|
return imposeChange (target, TimeVar(target) += offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal nudge a target time value by a step wise offset.
|
||||||
|
* The standard case uses a fixed offset of 1 second per step //////////////////TICKET #810
|
||||||
|
*/
|
||||||
|
inline TimeValue&
|
||||||
|
Mutation::imposeChange (TimeValue& target, int steps)
|
||||||
|
{
|
||||||
|
return imposeChange (target, TimeVar(target) += Time(FSecs(steps)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LIB_TIME_TIMEQUQNT_H
|
||||||
|
/** @internal Special treatment for quantised target values:
|
||||||
|
* use the quantised time's own grid; retrieve the corresponding grid point,
|
||||||
|
* offset it by the step-parameter, then retrieve the corresponding time from
|
||||||
|
* the quantised time's underlying quantiser (grid) and impose that as change.
|
||||||
|
* @note when the #steps parameter is zero, what happens here effectively
|
||||||
|
* is the materialisation of the quantised target time, i.e. making
|
||||||
|
* the quantisation explicit and storing the resulting value. */
|
||||||
|
inline TimeValue&
|
||||||
|
Mutation::imposeChange (QuTime& target, int steps)
|
||||||
|
{
|
||||||
|
PQuant const& grid (target);
|
||||||
|
int64_t originalGridPoint = grid->gridPoint(target);
|
||||||
|
int64_t adjustedGridPoint = originalGridPoint + steps;
|
||||||
|
return imposeChange (target, grid->timeOf (adjustedGridPoint));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
}} // lib::time
|
}} // lib::time
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,19 @@ namespace time {
|
||||||
, LUMIERA_ERROR_UNKNOWN_GRID);
|
, LUMIERA_ERROR_UNKNOWN_GRID);
|
||||||
return grid_found;
|
return grid_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
}//(End) implementation helpers
|
}//(End) implementation helpers
|
||||||
|
|
||||||
|
|
||||||
|
PQuant
|
||||||
|
getDefaultGridFallback()
|
||||||
|
{
|
||||||
|
static PQuant globalDefaultGrid (new FixedFrameQuantiser(1));
|
||||||
|
return globalDefaultGrid; ///////////////////////TICKET #810
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Grid::~Grid() { } // hint to emit the VTable here...
|
Grid::~Grid() { } // hint to emit the VTable here...
|
||||||
|
|
||||||
|
|
@ -191,7 +200,7 @@ namespace time {
|
||||||
{
|
{
|
||||||
Time gt(gridTime);
|
Time gt(gridTime);
|
||||||
TimeVar timePoint = gt + origin_;
|
TimeVar timePoint = gt + origin_;
|
||||||
timePoint += gridOffset * raster_;
|
timePoint += gridOffset * Offset(raster_);
|
||||||
return timePoint;
|
return timePoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,17 @@ namespace time {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @todo placeholder for accessing
|
||||||
|
* a current or default session grid.
|
||||||
|
* To be implemented later.
|
||||||
|
*/
|
||||||
|
PQuant getDefaultGridFallback(); ///////////////////////TICKET #810
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Facility to create grid-aligned time values.
|
* Facility to create grid-aligned time values.
|
||||||
* Effectively, a quantiser exposes the value Grid API, but
|
* Effectively, a quantiser exposes the value Grid API, but
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,11 @@ namespace time {
|
||||||
|
|
||||||
namespace error = lumiera::error;
|
namespace error = lumiera::error;
|
||||||
|
|
||||||
|
// forwards...
|
||||||
|
class FrameRate;
|
||||||
|
class TimeSpan;
|
||||||
|
class Mutation;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* basic constant internal time value.
|
* basic constant internal time value.
|
||||||
|
|
@ -193,6 +198,11 @@ namespace time {
|
||||||
: TimeValue(TimeVar(target) -= origin)
|
: TimeValue(TimeVar(target) -= origin)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
Offset (int64_t count, FrameRate const& fps);
|
||||||
|
|
||||||
|
static const Offset ZERO;
|
||||||
|
|
||||||
|
|
||||||
TimeValue
|
TimeValue
|
||||||
abs() const
|
abs() const
|
||||||
{
|
{
|
||||||
|
|
@ -241,7 +251,6 @@ namespace time {
|
||||||
* @warning do not mix up gavl_time_t and FSecs */
|
* @warning do not mix up gavl_time_t and FSecs */
|
||||||
typedef boost::rational<long> FSecs;
|
typedef boost::rational<long> FSecs;
|
||||||
|
|
||||||
class FrameRate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lumiera's internal time value datatype.
|
* Lumiera's internal time value datatype.
|
||||||
|
|
@ -304,8 +313,6 @@ namespace time {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TimeSpan;
|
|
||||||
class Mutation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duration is the internal Lumiera time metric.
|
* Duration is the internal Lumiera time metric.
|
||||||
|
|
@ -315,34 +322,64 @@ namespace time {
|
||||||
* possibility to send a \em Mutation message.
|
* possibility to send a \em Mutation message.
|
||||||
*/
|
*/
|
||||||
class Duration
|
class Duration
|
||||||
: public Offset
|
: public TimeValue
|
||||||
{
|
{
|
||||||
/// direct assignment prohibited
|
/// direct assignment prohibited
|
||||||
Duration& operator= (Duration const&);
|
Duration& operator= (Duration const&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Duration (Offset const& distance)
|
Duration (Offset const& distance)
|
||||||
: Offset(distance.abs())
|
: TimeValue(distance.abs())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
Duration (TimeValue const& timeSpec)
|
Duration (TimeValue const& timeSpec)
|
||||||
: Offset(Offset(timeSpec).abs())
|
: TimeValue(Offset(timeSpec).abs())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
Duration (FSecs const& timeSpan_in_secs)
|
Duration (FSecs const& timeSpan_in_secs)
|
||||||
: Offset(Offset(Time(timeSpan_in_secs)).abs())
|
: TimeValue(Offset(Time(timeSpan_in_secs)).abs())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
Duration (TimeSpan const& interval);
|
Duration (TimeSpan const& interval);
|
||||||
Duration (ulong count, FrameRate const& fps);
|
Duration (int64_t count, FrameRate const& fps);
|
||||||
|
|
||||||
static const Duration NIL;
|
static const Duration NIL;
|
||||||
|
|
||||||
void accept (Mutation const&);
|
void accept (Mutation const&);
|
||||||
|
|
||||||
|
/// Supporting backwards use as offset
|
||||||
|
Offset operator- () const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-- support using a Duration to build offsets ---------------
|
||||||
|
|
||||||
|
inline Duration
|
||||||
|
operator+ (Duration const& base, Duration const& toAdd)
|
||||||
|
{
|
||||||
|
return Offset(base) + Offset(toAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Offset
|
||||||
|
operator* (int factor, Duration const& dur)
|
||||||
|
{
|
||||||
|
return factor * Offset(dur);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Offset
|
||||||
|
operator* (Duration const& dur, int factor)
|
||||||
|
{
|
||||||
|
return factor*dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Offset
|
||||||
|
Duration::operator- () const
|
||||||
|
{
|
||||||
|
return -1 * (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -493,7 +530,7 @@ namespace time {
|
||||||
|
|
||||||
inline
|
inline
|
||||||
Duration::Duration (TimeSpan const& interval)
|
Duration::Duration (TimeSpan const& interval)
|
||||||
: Offset(interval.duration())
|
: TimeValue(interval.duration())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,8 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef CONTROL_TYPED_ALLOCATION_MANAGER_H
|
#ifndef LIB_TYPED_ALLOCATION_MANAGER_H
|
||||||
#define CONTROL_TYPED_ALLOCATION_MANAGER_H
|
#define LIB_TYPED_ALLOCATION_MANAGER_H
|
||||||
|
|
||||||
//#include "pre.hpp"
|
//#include "pre.hpp"
|
||||||
#include "lib/error.hpp"
|
#include "lib/error.hpp"
|
||||||
|
|
|
||||||
|
|
@ -37,15 +37,15 @@
|
||||||
using lib::Cmdline;
|
using lib::Cmdline;
|
||||||
using lumiera::Subsys;
|
using lumiera::Subsys;
|
||||||
using lumiera::AppState;
|
using lumiera::AppState;
|
||||||
using lumiera::ON_GLOBAL_INIT;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Subsys& engine = backend::EngineFacade::getDescriptor();
|
Subsys& engine = backend::EngineFacade::getDescriptor();
|
||||||
Subsys& netNode = backend::NetNodeFacade::getDescriptor();
|
Subsys& netNode = backend::NetNodeFacade::getDescriptor();
|
||||||
Subsys& script = backend::ScriptRunnerFacade::getDescriptor();
|
Subsys& script = backend::ScriptRunnerFacade::getDescriptor();
|
||||||
Subsys& player = lumiera::DummyPlayer::getDescriptor();
|
Subsys& player = lumiera::DummyPlayer::getDescriptor(); ///////TODO: just a dummy, until we're able to render
|
||||||
Subsys& builder = proc::Facade::getBuilderDescriptor();
|
Subsys& builder = proc::Facade::getBuilderDescriptor();
|
||||||
Subsys& session = proc::Facade::getSessionDescriptor();
|
Subsys& session = proc::Facade::getSessionDescriptor();
|
||||||
|
Subsys& playOut = proc::Facade::getPlayOutDescriptor();
|
||||||
Subsys& lumigui = gui::GuiFacade::getDescriptor();
|
Subsys& lumigui = gui::GuiFacade::getDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,16 +63,20 @@ main (int argc, const char* argv[])
|
||||||
lumiera::Option options (args);
|
lumiera::Option options (args);
|
||||||
application.init (options);
|
application.init (options);
|
||||||
|
|
||||||
session.depends (builder);
|
// session.depends (builder);
|
||||||
netNode.depends (session);
|
netNode.depends (session);
|
||||||
netNode.depends (engine);
|
netNode.depends (engine);
|
||||||
|
// playOut.depends (engine);
|
||||||
|
// playOut.depends (session);
|
||||||
// lumigui.depends (session); //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin
|
// lumigui.depends (session); //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin
|
||||||
// lumigui.depends (engine);
|
// lumigui.depends (engine);
|
||||||
|
player.depends (playOut); //////TODO dummy player, until we're able to render
|
||||||
lumigui.depends (player);
|
lumigui.depends (player);
|
||||||
script.depends (session);
|
script.depends (session);
|
||||||
script.depends (engine);
|
script.depends (engine);
|
||||||
|
|
||||||
application.maybeStart (session);
|
application.maybeStart (session);
|
||||||
|
application.maybeStart (playOut);
|
||||||
application.maybeStart (netNode);
|
application.maybeStart (netNode);
|
||||||
application.maybeStart (lumigui);
|
application.maybeStart (lumigui);
|
||||||
application.maybeStart (script);
|
application.maybeStart (script);
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
#include "lib/hash-indexed.hpp"
|
#include "lib/hash-indexed.hpp"
|
||||||
#include "lib/util.hpp"
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
|
#include <boost/functional/hash.hpp>
|
||||||
#include <boost/operators.hpp>
|
#include <boost/operators.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
||||||
70
src/proc/asset/viewer.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
Viewer - asset corresponding to a viewer element in the GUI
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
* *****************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "proc/asset/viewer.hpp"
|
||||||
|
//#include "proc/mobject/session/track.hpp"
|
||||||
|
//#include "proc/mobject/placement.hpp"
|
||||||
|
//#include "proc/mobject/session/mobjectfactory.hpp"
|
||||||
|
//#include "proc/mobject/session/binding.hpp"
|
||||||
|
//#include "proc/assetmanager.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace asset {
|
||||||
|
|
||||||
|
// using lib::AutoRegistered;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @todo anything significant to do here??? */
|
||||||
|
Viewer::Viewer (const Asset::Ident& idi)
|
||||||
|
: Struct (idi)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("anything regarding Viewer Assets");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//PViewer
|
||||||
|
//Viewer::create (Asset::Ident const& idi)
|
||||||
|
//{
|
||||||
|
// REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation");
|
||||||
|
//
|
||||||
|
// PTimeline newElement (AssetManager::instance().wrap (*new Viewer(idi)));
|
||||||
|
// getRegistry().append (newElement);
|
||||||
|
//
|
||||||
|
// ENSURE (newElement);
|
||||||
|
// ENSURE (getRegistry().isRegistered (*newElement));
|
||||||
|
// return newElement;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Viewer::unlink ()
|
||||||
|
{
|
||||||
|
// AutoRegistered<Timeline>::detach();
|
||||||
|
Struct::unlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace asset
|
||||||
107
src/proc/asset/viewer.hpp
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
VIEWER.hpp - asset corresponding to a viewer element in the GUI
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file viewer.hpp
|
||||||
|
** structural element corresponding to a viewer in the GUI.
|
||||||
|
** This Asset marks an attachment point, allowing other (output producing)
|
||||||
|
** elements to be connected to ("viewer attachment"). The typical standard case
|
||||||
|
** is a Timeline: It features a set of global pipes (busses), which collect all
|
||||||
|
** produced data. Yet without an explicit output connection, this data can't be
|
||||||
|
** generated, because in Lumiera, actual output is always retrieved ("pulled")
|
||||||
|
** starting from an output sink. Thus, in order to \em perform (play, render)
|
||||||
|
** the timeline, an "view connection" needs to be established: this connection
|
||||||
|
** is represented by an session::BindingMO, linking the Timeline to an
|
||||||
|
** asset::Viewer. Consequently, the same output mapping and translation
|
||||||
|
** mechanism used for Sequence-Timeline- (and VirtualClip-)Bindings
|
||||||
|
** is employed in this situation to figure out the real output port.
|
||||||
|
**
|
||||||
|
** @todo WIP-WIP-WIP as of 5/11
|
||||||
|
**
|
||||||
|
** @see Session
|
||||||
|
** @see Timeline
|
||||||
|
** @see StructFactory
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ASSET_VIEWER_H
|
||||||
|
#define ASSET_VIEWER_H
|
||||||
|
|
||||||
|
#include "proc/asset/struct.hpp"
|
||||||
|
//#include "proc/mobject/mobject.hpp"
|
||||||
|
//#include "proc/mobject/placement.hpp"
|
||||||
|
#include "proc/mobject/mobject-ref.hpp"
|
||||||
|
//#include "proc/mobject/session/binding.hpp" ////TODO avoidable??
|
||||||
|
#include "lib/p.hpp"
|
||||||
|
#include "lib/element-tracker.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
//#include <vector>
|
||||||
|
//#include <string>
|
||||||
|
|
||||||
|
//using std::vector;
|
||||||
|
//using std::string;
|
||||||
|
|
||||||
|
namespace mobject {
|
||||||
|
namespace session {
|
||||||
|
|
||||||
|
class Binding;
|
||||||
|
typedef MORef<Binding> RBinding;
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
|
namespace asset {
|
||||||
|
|
||||||
|
|
||||||
|
using lumiera::P;
|
||||||
|
class Viewer;
|
||||||
|
typedef lumiera::P<Viewer> PViewer;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO type comment
|
||||||
|
*/
|
||||||
|
class Viewer
|
||||||
|
: public Struct
|
||||||
|
// , public lib::AutoRegistered<Viewer>
|
||||||
|
{
|
||||||
|
|
||||||
|
Viewer (Ident const&); /////TODO ctor params????
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** create and register a new Timeline instance */
|
||||||
|
// static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void unlink ();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////TODO currently just fleshing the API
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace asset
|
||||||
|
#endif
|
||||||
|
|
@ -54,7 +54,6 @@
|
||||||
|
|
||||||
namespace control {
|
namespace control {
|
||||||
|
|
||||||
using lib::TypedAllocationManager;
|
|
||||||
using lib::InPlaceBuffer;
|
using lib::InPlaceBuffer;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
#include "lib/bool-checkable.hpp"
|
#include "lib/bool-checkable.hpp"
|
||||||
#include "lib/meta/function.hpp"
|
#include "lib/meta/function.hpp"
|
||||||
#include "lib/meta/typelist.hpp"
|
#include "lib/meta/typelist.hpp"
|
||||||
#include "lib/meta/typelist-util.hpp"
|
#include "lib/meta/typelist-manip.hpp"
|
||||||
#include "lib/meta/tuple.hpp"
|
#include "lib/meta/tuple.hpp"
|
||||||
#include "lib/util.hpp"
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@
|
||||||
|
|
||||||
namespace control {
|
namespace control {
|
||||||
|
|
||||||
using lib::TypedAllocationManager;
|
|
||||||
using std::tr1::function;
|
using std::tr1::function;
|
||||||
using std::tr1::shared_ptr;
|
using std::tr1::shared_ptr;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
** we need to accept functor objects with a very specific and predetermined
|
** we need to accept functor objects with a very specific and predetermined
|
||||||
** signature, thus allowing for strict type checking by the compiler.
|
** signature, thus allowing for strict type checking by the compiler.
|
||||||
**
|
**
|
||||||
** \par Relation of function signatures
|
** \par Relation of function signatures (MEM = type of the "memento" for Undo)
|
||||||
** - operation: void(P1,..PN)
|
** - operation: void(P1,..PN)
|
||||||
** - captureUndo: MEM(P1,..PN)
|
** - captureUndo: MEM(P1,..PN)
|
||||||
** - undoOperation void(P1,..PN,MEM)
|
** - undoOperation void(P1,..PN,MEM)
|
||||||
|
|
@ -47,14 +47,11 @@
|
||||||
#define CONTROL_COMMAND_SIGNATURE_H
|
#define CONTROL_COMMAND_SIGNATURE_H
|
||||||
|
|
||||||
//#include "pre.hpp"
|
//#include "pre.hpp"
|
||||||
//#include "lib/symbol.hpp"
|
|
||||||
#include "lib/meta/function.hpp"
|
#include "lib/meta/function.hpp"
|
||||||
#include "lib/meta/typelist.hpp"
|
#include "lib/meta/typelist.hpp"
|
||||||
#include "lib/meta/typelist-util.hpp"
|
#include "lib/meta/typelist-manip.hpp"
|
||||||
#include "lib/meta/typeseq-util.hpp"
|
#include "lib/meta/typeseq-util.hpp"
|
||||||
//#include "lib/meta/tuple.hpp"
|
|
||||||
|
|
||||||
//#include <tr1/memory>
|
|
||||||
#include <tr1/functional>
|
#include <tr1/functional>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -62,15 +59,11 @@
|
||||||
|
|
||||||
namespace control {
|
namespace control {
|
||||||
|
|
||||||
// using lib::Symbol;
|
|
||||||
// using std::tr1::shared_ptr;
|
|
||||||
using std::tr1::function;
|
using std::tr1::function;
|
||||||
|
|
||||||
using lumiera::typelist::FunctionSignature;
|
using lumiera::typelist::FunctionSignature;
|
||||||
using lumiera::typelist::FunctionTypedef;
|
using lumiera::typelist::FunctionTypedef;
|
||||||
using lumiera::typelist::Types;
|
using lumiera::typelist::Types;
|
||||||
//using lumiera::typelist::NullType;
|
|
||||||
//using lumiera::typelist::Tuple;
|
|
||||||
using lumiera::typelist::Append;
|
using lumiera::typelist::Append;
|
||||||
using lumiera::typelist::SplitLast;
|
using lumiera::typelist::SplitLast;
|
||||||
|
|
||||||
|
|
@ -79,7 +72,7 @@ namespace control {
|
||||||
* Metaprogramming helper for building Command function signatures.
|
* Metaprogramming helper for building Command function signatures.
|
||||||
* The complete definition context of any command is templated to the signature
|
* The complete definition context of any command is templated to the signature
|
||||||
* of the actual command operation and to the memento type. The typedefs embedded
|
* of the actual command operation and to the memento type. The typedefs embedded
|
||||||
* within CommandSignature<SIG,MEM> allows accepting suitable typed functions
|
* within CommandSignature<SIG,MEM> allows for accepting suitable typed functions
|
||||||
* to implement the command in question.
|
* to implement the command in question.
|
||||||
*/
|
*/
|
||||||
template<typename SIG, typename MEM>
|
template<typename SIG, typename MEM>
|
||||||
|
|
@ -109,10 +102,17 @@ namespace control {
|
||||||
* and the signature necessary for capturing undo information. The implementation
|
* and the signature necessary for capturing undo information. The implementation
|
||||||
* relies on re-binding an embedded type defining template, based on the actual
|
* relies on re-binding an embedded type defining template, based on the actual
|
||||||
* case, as identified by the structure of the given parameter signature.
|
* case, as identified by the structure of the given parameter signature.
|
||||||
|
*
|
||||||
|
* To use this template, it is instantiated with the signature of a functor object
|
||||||
|
* in question. Depending on the actual situation, the compiler will then either
|
||||||
|
* pick Case1 or Case2 -- thus allowing the client in any case to pick up the
|
||||||
|
* correct signatures for Operation, Capture and Undo-function from the
|
||||||
|
* public typedefs within \c UndoSignature
|
||||||
*/
|
*/
|
||||||
template<typename SIG>
|
template<typename SIG>
|
||||||
class UndoSignature
|
class UndoSignature
|
||||||
{
|
{
|
||||||
|
// preparation: dissect the function signature into arguments and result
|
||||||
typedef typename FunctionSignature< function<SIG> >::Args Args;
|
typedef typename FunctionSignature< function<SIG> >::Args Args;
|
||||||
typedef typename FunctionSignature< function<SIG> >::Ret Ret;
|
typedef typename FunctionSignature< function<SIG> >::Ret Ret;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ namespace control {
|
||||||
* It is returned when invoking a HandlingPattern
|
* It is returned when invoking a HandlingPattern
|
||||||
* and can be used to check for success and/or re-throw
|
* and can be used to check for success and/or re-throw
|
||||||
* any Exception encountered during the command execution.
|
* any Exception encountered during the command execution.
|
||||||
|
* @todo couldn't that be replaced by a lib::Result<void> instance??
|
||||||
*/
|
*/
|
||||||
class ExecResult
|
class ExecResult
|
||||||
: public lib::BoolCheckable<ExecResult>
|
: public lib::BoolCheckable<ExecResult>
|
||||||
|
|
|
||||||
584
src/proc/engine/buffer-metadata.hpp
Normal file
|
|
@ -0,0 +1,584 @@
|
||||||
|
/*
|
||||||
|
BUFFER-METADATA.hpp - internal metadata for data buffer providers
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file buffer-metadate.hpp
|
||||||
|
** Metadata for managing and accessing buffers. The Lumiera Engine uses the
|
||||||
|
** Abstraction of an BufferProvider to handle various kinds of buffer organisation
|
||||||
|
** and access in a uniform way. Actually, buffers can be exposed and provided by several
|
||||||
|
** facilities, which might even be implemented through an external library. Thus the engine
|
||||||
|
** and the abstraction placed in between needs a common set of control data, to be able to
|
||||||
|
** expose the correct buffer for each request. Typically -- and independent of the actual
|
||||||
|
** implementation -- the following properties need to be tracked
|
||||||
|
** - that overall storage size available within the buffer
|
||||||
|
** - a pair of custom \em creator and \em destructor functions to use together with this buffer
|
||||||
|
** - an additional client key to distinguish otherwise otherwise identical client requests
|
||||||
|
** These three distinctions are applied in sequence, thus forming a tree with 3 levels.
|
||||||
|
** Only the first distinguishing level (the size) is mandatory. The others are provided,
|
||||||
|
** because some of the foreseeable buffer providers allow to re-access the data placed
|
||||||
|
** into the buffer, by assigning an internally managed ID to the buffer. The most
|
||||||
|
** prominent example is the frame cache, which obviously needs to keep track of
|
||||||
|
** the buffers after the render engine is finished, while the engine code
|
||||||
|
** just accesses yet another buffer to place the results of calculations.
|
||||||
|
**
|
||||||
|
** These additional distinctions and properties are associated with the help of the
|
||||||
|
** BufferDescriptor, embedded into each BuffHandle. While the engine just uses these
|
||||||
|
** handles in the way of a pointer, the buffer descriptor acts as an additional tag
|
||||||
|
** attached to the buffer access, allowing to re-access a context within the
|
||||||
|
** buffer provider implementation.
|
||||||
|
**
|
||||||
|
** @see buffer-provider.hpp
|
||||||
|
** @see BufferMetadata_test
|
||||||
|
** @see BufferProviderProtocol_test
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROC_ENGINE_BUFFR_METADATA_H
|
||||||
|
#define PROC_ENGINE_BUFFR_METADATA_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "lib/symbol.hpp"
|
||||||
|
#include "lib/functor-util.hpp"
|
||||||
|
|
||||||
|
#include <tr1/functional>
|
||||||
|
#include <boost/functional/hash.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
using lib::HashVal;
|
||||||
|
using lib::Literal;
|
||||||
|
using std::tr1::bind;
|
||||||
|
using std::tr1::function;
|
||||||
|
using std::tr1::placeholders::_1;
|
||||||
|
|
||||||
|
namespace error = lumiera::error;
|
||||||
|
|
||||||
|
namespace metadata {
|
||||||
|
class Key;
|
||||||
|
class Entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum BufferState
|
||||||
|
{ NIL,
|
||||||
|
FREE,
|
||||||
|
LOCKED,
|
||||||
|
EMITTED,
|
||||||
|
BLOCKED
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* an opaque ID to be used by the BufferProvider implementation.
|
||||||
|
* Typically this will be used, to set apart some pre-registered
|
||||||
|
* kinds of buffers. It is treated as being part of the buffer type.
|
||||||
|
* LocalKey objects may be copied but not re-assigned or changed.
|
||||||
|
*/
|
||||||
|
class LocalKey
|
||||||
|
{
|
||||||
|
uint64_t privateID_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LocalKey (uint64_t opaqueValue=0)
|
||||||
|
: privateID_(opaqueValue)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
operator uint64_t() const { return privateID_; }
|
||||||
|
|
||||||
|
friend size_t
|
||||||
|
hash_value (LocalKey const& lkey)
|
||||||
|
{
|
||||||
|
boost::hash<uint64_t> hashFunction;
|
||||||
|
return hashFunction(lkey.privateID_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** assignment usually prohibited */
|
||||||
|
LocalKey& operator= (LocalKey const& o)
|
||||||
|
{
|
||||||
|
privateID_ = o.privateID_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** but Key assignments are acceptable */
|
||||||
|
friend class metadata::Key;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace { // Helpers for construction within the buffer...
|
||||||
|
|
||||||
|
template<class X>
|
||||||
|
inline void
|
||||||
|
buildIntoBuffer (void* storageBuffer)
|
||||||
|
{
|
||||||
|
new(storageBuffer) X();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class X, typename A1>
|
||||||
|
inline void
|
||||||
|
buildIntoBuffer_A1 (void* storageBuffer, A1 arg1)
|
||||||
|
{
|
||||||
|
new(storageBuffer) X(arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class X>
|
||||||
|
inline void
|
||||||
|
destroyInBuffer (void* storageBuffer)
|
||||||
|
{
|
||||||
|
X* embedded = static_cast<X*> (storageBuffer);
|
||||||
|
embedded->~X();
|
||||||
|
}
|
||||||
|
}//(End)placement-new helpers
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pair of functors to maintain a datastructure within the buffer.
|
||||||
|
* TypeHandler describes how to outfit the buffer in a specific way.
|
||||||
|
* When defined, the buffer will be prepared when locking and cleanup
|
||||||
|
* will be invoked automatically when releasing. Especially, this
|
||||||
|
* can be used to \em attach an object to the buffer (placement-new)
|
||||||
|
*/
|
||||||
|
struct TypeHandler
|
||||||
|
{
|
||||||
|
typedef function<void(void*)> DoInBuffer;
|
||||||
|
|
||||||
|
DoInBuffer createAttached;
|
||||||
|
DoInBuffer destroyAttached;
|
||||||
|
|
||||||
|
/** build an invalid NIL TypeHandler */
|
||||||
|
TypeHandler()
|
||||||
|
: createAttached()
|
||||||
|
, destroyAttached()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/** build a TypeHandler
|
||||||
|
* binding to arbitrary constructor and destructor functions.
|
||||||
|
* On invocation, these functions get a void* to the buffer.
|
||||||
|
* @note the functor objects created from these operations
|
||||||
|
* might be shared for handling multiple buffers.
|
||||||
|
* Be careful with any state or arguments.
|
||||||
|
*/
|
||||||
|
template<typename CTOR, typename DTOR>
|
||||||
|
TypeHandler(CTOR ctor, DTOR dtor)
|
||||||
|
: createAttached (ctor)
|
||||||
|
, destroyAttached (dtor)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/** builder function defining a TypeHandler
|
||||||
|
* to place a default-constructed object
|
||||||
|
* into the buffer. */
|
||||||
|
template<class X>
|
||||||
|
static TypeHandler
|
||||||
|
create ()
|
||||||
|
{
|
||||||
|
return TypeHandler (buildIntoBuffer<X>, destroyInBuffer<X>);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class X, typename A1>
|
||||||
|
static TypeHandler
|
||||||
|
create (A1 a1)
|
||||||
|
{
|
||||||
|
return TypeHandler ( bind (buildIntoBuffer_A1<X,A1>, _1, a1)
|
||||||
|
, destroyInBuffer<X>);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isValid() const
|
||||||
|
{
|
||||||
|
return bool(createAttached)
|
||||||
|
&& bool(destroyAttached);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend HashVal
|
||||||
|
hash_value (TypeHandler const& handler)
|
||||||
|
{
|
||||||
|
HashVal hash(0);
|
||||||
|
if (handler.isValid())
|
||||||
|
{
|
||||||
|
boost::hash_combine(hash, handler.createAttached);
|
||||||
|
boost::hash_combine(hash, handler.destroyAttached);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool
|
||||||
|
operator== (TypeHandler const& left, TypeHandler const& right)
|
||||||
|
{
|
||||||
|
return (!left.isValid() && !right.isValid())
|
||||||
|
|| ( util::rawComparison(left.createAttached, right.createAttached)
|
||||||
|
&& util::rawComparison(left.destroyAttached, right.destroyAttached)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
friend bool
|
||||||
|
operator!= (TypeHandler const& left, TypeHandler const& right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace { // internal constants to mark the default case
|
||||||
|
|
||||||
|
const LocalKey UNSPECIFIC;
|
||||||
|
const TypeHandler RAW_BUFFER;
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
nontrivial (TypeHandler const& toVerify)
|
||||||
|
{
|
||||||
|
return RAW_BUFFER != toVerify;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
nontrivial (LocalKey const& toVerify)
|
||||||
|
{
|
||||||
|
return UNSPECIFIC != toVerify;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === Implementation === */
|
||||||
|
|
||||||
|
namespace metadata {
|
||||||
|
|
||||||
|
using error::LUMIERA_ERROR_LIFECYCLE;
|
||||||
|
using error::LUMIERA_ERROR_BOTTOM_VALUE;
|
||||||
|
|
||||||
|
namespace { // details of hash calculation
|
||||||
|
template<typename VAL>
|
||||||
|
HashVal
|
||||||
|
chainedHash(HashVal accumulatedHash, VAL changedValue)
|
||||||
|
{
|
||||||
|
boost::hash_combine (accumulatedHash, changedValue);
|
||||||
|
return accumulatedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Key
|
||||||
|
{
|
||||||
|
HashVal parent_;
|
||||||
|
HashVal hashID_;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
size_t storageSize_;
|
||||||
|
TypeHandler instanceFunc_;
|
||||||
|
LocalKey specifics_;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** build a standard basic key describing a kind of Buffer.
|
||||||
|
* @param familyID basic hash seed value to distinguish
|
||||||
|
* families of buffer types managed by
|
||||||
|
* different BufferProvider instances
|
||||||
|
* @param storageSize fundamental info: buffer size
|
||||||
|
*/
|
||||||
|
Key (HashVal familyID, size_t storageSize)
|
||||||
|
: parent_(familyID)
|
||||||
|
, hashID_(chainedHash (familyID, storageSize))
|
||||||
|
, storageSize_(storageSize)
|
||||||
|
, instanceFunc_(RAW_BUFFER)
|
||||||
|
, specifics_(UNSPECIFIC)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// standard copy operations permitted
|
||||||
|
|
||||||
|
/** create a derived buffer type description.
|
||||||
|
* Using a different storage size than the parent type,
|
||||||
|
* all else remaining the same
|
||||||
|
*/
|
||||||
|
Key (Key const& parent, size_t differingStorageSize)
|
||||||
|
: parent_(parent.hashID_)
|
||||||
|
, hashID_(chainedHash (parent_, differingStorageSize))
|
||||||
|
, storageSize_(differingStorageSize) // differing from parent
|
||||||
|
, instanceFunc_(parent.instanceFunc_)
|
||||||
|
, specifics_(parent.specifics_)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/** create a derived buffer type description.
|
||||||
|
* Using different ctor and dtor functions,
|
||||||
|
* all else remaining the same as with parent
|
||||||
|
*/
|
||||||
|
Key (Key const& parent, TypeHandler const& differingTypeHandlerFunctions)
|
||||||
|
: parent_(parent.hashID_)
|
||||||
|
, hashID_(chainedHash (parent_, differingTypeHandlerFunctions))
|
||||||
|
, storageSize_(parent.storageSize_)
|
||||||
|
, instanceFunc_(differingTypeHandlerFunctions) // differing from parent
|
||||||
|
, specifics_(parent.specifics_)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/** create a derived buffer type description.
|
||||||
|
* Using a different private ID than the parent type,
|
||||||
|
* all else remaining the same
|
||||||
|
*/
|
||||||
|
Key (Key const& parent, LocalKey anotherTypeSpecificInternalID)
|
||||||
|
: parent_(parent.hashID_)
|
||||||
|
, hashID_(chainedHash (parent_, anotherTypeSpecificInternalID))
|
||||||
|
, storageSize_(parent.storageSize_)
|
||||||
|
, instanceFunc_(parent.instanceFunc_)
|
||||||
|
, specifics_(anotherTypeSpecificInternalID) // differing from parent
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
HashVal parentKey() const { return parent_;}
|
||||||
|
operator HashVal() const { return hashID_;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Entry
|
||||||
|
: public Key
|
||||||
|
{
|
||||||
|
BufferState state_;
|
||||||
|
const void* buffer_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual BufferState
|
||||||
|
state() const
|
||||||
|
{
|
||||||
|
__must_not_be_NIL();
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const void*
|
||||||
|
access() const
|
||||||
|
{
|
||||||
|
__must_not_be_NIL();
|
||||||
|
__must_not_be_FREE();
|
||||||
|
|
||||||
|
ENSURE (buffer_);
|
||||||
|
return buffer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Entry&
|
||||||
|
mark (BufferState newState)
|
||||||
|
{
|
||||||
|
switch (this->state_)
|
||||||
|
{
|
||||||
|
case NIL: __must_not_be_NIL();
|
||||||
|
case FREE: __must_not_be_FREE();
|
||||||
|
|
||||||
|
case LOCKED:
|
||||||
|
if (newState == EMITTED) break; // allow transition
|
||||||
|
|
||||||
|
case EMITTED:
|
||||||
|
if (newState == BLOCKED) break; // allow transition
|
||||||
|
|
||||||
|
case BLOCKED:
|
||||||
|
if (newState == FREE) // note fall through for LOCKED and EMITTED too
|
||||||
|
{
|
||||||
|
buffer_ = 0;
|
||||||
|
break; // allow transition
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw error::Fatal ("Invalid buffer state encountered.");
|
||||||
|
}
|
||||||
|
state_ = newState;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
__must_not_be_NIL() const
|
||||||
|
{
|
||||||
|
if (NIL == state_)
|
||||||
|
throw error::Fatal ("Concrete buffer entry with state==NIL encountered."
|
||||||
|
"State transition logic broken (programming error)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
__must_not_be_FREE() const
|
||||||
|
{
|
||||||
|
if (FREE == state_)
|
||||||
|
throw error::Logic ("Buffer is inaccessible (marked as free). "
|
||||||
|
"Need a new buffer pointer in order to lock an entry. "
|
||||||
|
"You should invoke markLocked(buffer) prior to access."
|
||||||
|
, LUMIERA_ERROR_LIFECYCLE );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Metadata
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
Literal id_;
|
||||||
|
HashVal family_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef metadata::Key Key;
|
||||||
|
typedef metadata::Entry Entry;
|
||||||
|
|
||||||
|
/** establish a metadata registry.
|
||||||
|
* Such will maintain a family of buffer type entries
|
||||||
|
* and provide a service for storing and retrieving metadata
|
||||||
|
* for concrete buffer entries associated with these types.
|
||||||
|
* @param implementationID to distinguish families
|
||||||
|
* of type keys belonging to different registries.
|
||||||
|
*/
|
||||||
|
Metadata (Literal implementationID)
|
||||||
|
: id_(implementationID)
|
||||||
|
, family_(hash_value(id_))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/** combine the distinguishing properties
|
||||||
|
* into a single type key, which will be known/remembered
|
||||||
|
* from that point on. Properties are combined according to
|
||||||
|
* a fixed type specialisation order, with the buffer size
|
||||||
|
* forming the base level, possible TypeHandler functors the
|
||||||
|
* second level, and implementation defined LocalKey entries
|
||||||
|
* the third level. All these levels describe abstract type
|
||||||
|
* keys, not entries for concrete buffers. The latter are
|
||||||
|
* always created as children of a known type key.
|
||||||
|
*/
|
||||||
|
Key
|
||||||
|
key ( size_t storageSize
|
||||||
|
, TypeHandler instanceFunc =RAW_BUFFER
|
||||||
|
, LocalKey specifics =UNSPECIFIC)
|
||||||
|
{
|
||||||
|
REQUIRE (storageSize);
|
||||||
|
Key typeKey = trackKey (family_, storageSize);
|
||||||
|
|
||||||
|
if (nontrivial(instanceFunc))
|
||||||
|
{
|
||||||
|
typeKey = trackKey (typeKey, instanceFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nontrivial(specifics))
|
||||||
|
{
|
||||||
|
typeKey = trackKey (typeKey, specifics);
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** create a sub-type, using a different type/handler functor */
|
||||||
|
Key
|
||||||
|
key (Key const& parentKey, TypeHandler const& instanceFunc)
|
||||||
|
{
|
||||||
|
return trackKey (parentKey, instanceFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** create a sub-type, using a different private-ID (implementation defined) */
|
||||||
|
Key
|
||||||
|
key (Key const& parentKey, LocalKey specifics)
|
||||||
|
{
|
||||||
|
return trackKey (parentKey, specifics);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key
|
||||||
|
key (Key const& parentKey, const void* concreteBuffer)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("create sub-object key for concrete buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
Key const&
|
||||||
|
get (HashVal hashID)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("access the plain key entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry&
|
||||||
|
get (Key key)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("access, possibly create metadata records");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isKnown (HashVal key) const
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("diagnostics: known record?");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isLocked (HashVal key) const
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* == memory management == */
|
||||||
|
|
||||||
|
Entry& markLocked (Key const& parentKey, const void* buffer);
|
||||||
|
void release (HashVal key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
template<typename PAR, typename DEF>
|
||||||
|
Key
|
||||||
|
trackKey (PAR parent, DEF specialisation)
|
||||||
|
{
|
||||||
|
Key newKey (parent,specialisation);
|
||||||
|
maybeStore (newKey);
|
||||||
|
return newKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybeStore (Key const& key)
|
||||||
|
{
|
||||||
|
if (isKnown (key)) return;
|
||||||
|
UNIMPLEMENTED ("registry for type keys");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** */
|
||||||
|
inline Metadata::Entry&
|
||||||
|
Metadata::markLocked (Key const& parentKey, const void* buffer)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("transition to locked state");
|
||||||
|
if (!buffer)
|
||||||
|
throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
|
||||||
|
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||||
|
|
||||||
|
Key newKey = this->key (parentKey, buffer);
|
||||||
|
if (isLocked(newKey))
|
||||||
|
throw error::Logic ("Attempt to lock a slot for a new buffer, "
|
||||||
|
"while actually the old buffer is still locked."
|
||||||
|
, error::LUMIERA_ERROR_LIFECYCLE );
|
||||||
|
|
||||||
|
return this->get(newKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
Metadata::release (HashVal key)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("metadata memory management");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
|
#endif
|
||||||
82
src/proc/engine/buffer-provider.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
BufferProvider - Abstraction for Buffer management during playback/render
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
* *****************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "proc/engine/buffer-provider.hpp"
|
||||||
|
#include "proc/engine/buffer-metadata.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
|
||||||
|
namespace { // impl. details and definitions
|
||||||
|
|
||||||
|
const uint DEFAULT_DESCRIPTOR = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BufferProvider::BufferProvider (Literal implementationID)
|
||||||
|
: meta_(new Metadata (implementationID))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BufferProvider::~BufferProvider() { }
|
||||||
|
|
||||||
|
|
||||||
|
/** @internal verify the given descriptor.
|
||||||
|
* @return true if it corresponds to a buffer
|
||||||
|
* currently locked and usable by client code
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
BufferProvider::verifyValidity (BufferDescriptor const&)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("BufferProvider basic and default implementation");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BufferDescriptor
|
||||||
|
BufferProvider::getDescriptorFor (size_t storageSize)
|
||||||
|
{
|
||||||
|
return BufferDescriptor (*this, meta_->key (storageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === BufferDescriptor and BuffHandle === */
|
||||||
|
|
||||||
|
bool
|
||||||
|
BufferDescriptor::verifyValidity() const
|
||||||
|
{
|
||||||
|
return provider_->verifyValidity(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BuffHandle::release()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("forward buffer release call to buffer provider");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
135
src/proc/engine/buffer-provider.hpp
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
BUFFER-PROVIDER.hpp - Abstraction for Buffer management during playback/render
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file buffer-provider.hpp
|
||||||
|
** Abstraction to represent buffer management and lifecycle within the render engine.
|
||||||
|
** It turns out that -- throughout the render engine implementation -- we never need
|
||||||
|
** direct access to the buffers holding media data. Buffers are just some entity to be \em managed,
|
||||||
|
** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementation detail.
|
||||||
|
** The code within the render engine just pushes around BufferHandle objects, which act as a front-end,
|
||||||
|
** being created by and linked to a BufferProvider implementation. There is no need to manage the lifecycle
|
||||||
|
** of buffers automatically, because the use of buffers is embedded into the render calculation cycle,
|
||||||
|
** which follows a rather strict protocol anyway. Relying on the capabilities of the scheduler,
|
||||||
|
** the sequence of individual jobs in the engine ensures...
|
||||||
|
** - that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
|
||||||
|
** - that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
|
||||||
|
** - that buffers are marked as free ("released") after doing the actual calculations.
|
||||||
|
**
|
||||||
|
** @see state.hpp
|
||||||
|
** @see output-slot.hpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROC_ENGINE_BUFFR_PROVIDER_H
|
||||||
|
#define PROC_ENGINE_BUFFR_PROVIDER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "lib/symbol.hpp"
|
||||||
|
#include "proc/engine/buffhandle.hpp"
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
using boost::scoped_ptr;
|
||||||
|
using lib::Literal;
|
||||||
|
|
||||||
|
|
||||||
|
class Metadata;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface: a facility providing and managing working buffers for media calculations.
|
||||||
|
* The pointer to actual buffer storage can be retrieved by
|
||||||
|
* - optionally announcing the required buffer(s) beforehand
|
||||||
|
* - "locking" a buffer to yield a buffer handle
|
||||||
|
* - dereferencing this smart-handle class
|
||||||
|
*
|
||||||
|
* @todo as of 6/2011 buffer management within the engine is still a bit vague
|
||||||
|
*/
|
||||||
|
class BufferProvider
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
scoped_ptr<Metadata> meta_;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BufferProvider (Literal implementationID);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~BufferProvider(); ///< this is an interface
|
||||||
|
|
||||||
|
|
||||||
|
virtual uint announce (uint count, BufferDescriptor const&) =0;
|
||||||
|
|
||||||
|
virtual BuffHandle lockBufferFor (BufferDescriptor const&) =0;
|
||||||
|
virtual void releaseBuffer (BuffHandle const&) =0;
|
||||||
|
|
||||||
|
template<typename BU>
|
||||||
|
BuffHandle lockBufferFor ();
|
||||||
|
|
||||||
|
|
||||||
|
/** describe the kind of buffer managed by this provider */
|
||||||
|
BufferDescriptor getDescriptorFor(size_t storageSize=0);
|
||||||
|
|
||||||
|
template<typename BU>
|
||||||
|
BufferDescriptor getDescriptor();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === API for BuffHandle internal access === */
|
||||||
|
|
||||||
|
bool verifyValidity (BufferDescriptor const&);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === Implementation === */
|
||||||
|
|
||||||
|
/** convenience shortcut:
|
||||||
|
* prepare and claim ("lock") a buffer suitable
|
||||||
|
* to hold an object of the given type.
|
||||||
|
* @return a handle embedding a suitably configured
|
||||||
|
* buffer descriptor. The corresponding buffer
|
||||||
|
* has been allocated and marked for exclusive use
|
||||||
|
*/
|
||||||
|
template<typename BU>
|
||||||
|
BuffHandle
|
||||||
|
BufferProvider::lockBufferFor()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("convenience shortcut to announce and lock for a specific object type");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename BU>
|
||||||
|
BufferDescriptor
|
||||||
|
BufferProvider::getDescriptor()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("build descriptor for automatically placing an object instance into the buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
|
#endif
|
||||||
|
|
@ -24,11 +24,16 @@
|
||||||
** Various bits needed to support the buffer management within the render nodes.
|
** Various bits needed to support the buffer management within the render nodes.
|
||||||
** When pulling data from predecessor nodes and calculating new data, each render node
|
** When pulling data from predecessor nodes and calculating new data, each render node
|
||||||
** needs several input and output buffers. These may be allocated and provided by several
|
** needs several input and output buffers. These may be allocated and provided by several
|
||||||
** different "buffer providers" (for example the frame cache). For accessing those buffers,
|
** different "buffer providers" (for example the frame cache). Typically, the real buffers
|
||||||
** the node needs to keep a table of buffer pointers, and for releasing the buffers later
|
** will be passed as parameters to the actual job instance when scheduled, drawing on the
|
||||||
** on, we need some handles. The usage pattern of those buffer pointer tables is stack-like,
|
** results of prerequisite jobs. Yet the actual job implementation remains agnostic with
|
||||||
** thus it makes sense to utilise a single large buffer pointer array per pull() calldown
|
** respect to the way actual buffers are provided; the invocation just pushes BuffHandle
|
||||||
** sequence and dynamically claim small chunks for each node.
|
** objects around. The actual render function gets an array of C-pointers to the actual
|
||||||
|
** buffers, and for accessing those buffers, the node needs to keep a table of buffer
|
||||||
|
** pointers, and for releasing the buffers later on, we utilise the buffer handles.
|
||||||
|
** The usage pattern of those buffer pointer tables is stack-like, thus the actual
|
||||||
|
** implementation utilises a single large buffer pointer array per pull() call
|
||||||
|
** sequence and dynamically claims small chunks for each node.
|
||||||
**
|
**
|
||||||
** @see nodewiring-def.hpp
|
** @see nodewiring-def.hpp
|
||||||
** @see nodeoperation.hpp
|
** @see nodeoperation.hpp
|
||||||
|
|
@ -47,67 +52,48 @@
|
||||||
|
|
||||||
namespace engine {
|
namespace engine {
|
||||||
|
|
||||||
|
class BufferProvider;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle for a buffer for processing data, abstracting away the actual implementation.
|
* An opaque descriptor to identify the type and further properties of a data buffer.
|
||||||
* The real buffer pointer can be retrieved by dereferencing this smart-handle class.
|
* For each kind of buffer, there is somewhere a BufferProvider responsible for the
|
||||||
|
* actual storage management. This provider may "lock" a buffer for actual use,
|
||||||
|
* returning a BuffHandle.
|
||||||
|
* @note this descriptor and especially the #subClassification_ is really owned
|
||||||
|
* by the BufferProvider, which may use (and even change) the opaque contents
|
||||||
|
* to organise the internal buffer management.
|
||||||
|
*
|
||||||
|
* @todo try to move that definition into buffer-provider.hpp ////////////////////////////////////TICKET #249
|
||||||
*/
|
*/
|
||||||
struct BuffHandle
|
class BufferDescriptor
|
||||||
: lib::BoolCheckable<BuffHandle>
|
|
||||||
{
|
{
|
||||||
typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
|
BufferProvider* provider_;
|
||||||
typedef Buff* PBuff;
|
uint64_t subClassification_;
|
||||||
|
|
||||||
PBuff
|
BufferDescriptor(BufferProvider& manager, uint64_t detail)
|
||||||
operator->() const
|
: provider_(&manager)
|
||||||
{
|
, subClassification_(detail)
|
||||||
return pBuffer_;
|
|
||||||
}
|
|
||||||
Buff&
|
|
||||||
operator* () const
|
|
||||||
{
|
|
||||||
ENSURE (pBuffer_);
|
|
||||||
return *pBuffer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
isValid() const
|
|
||||||
{
|
|
||||||
return pBuffer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> Ticket 249
|
|
||||||
BuffHandle()
|
|
||||||
: pBuffer_(0),
|
|
||||||
sourceID_(0)
|
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
private:
|
friend class BufferProvider;
|
||||||
PBuff pBuffer_;
|
|
||||||
long sourceID_;
|
public:
|
||||||
|
// using standard copy operations
|
||||||
|
|
||||||
|
bool verifyValidity() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffer Type information.
|
|
||||||
* Given a BufferDescriptor, it is possible to allocate a buffer
|
|
||||||
* of suitable size and type by using State::allocateBuffer().
|
|
||||||
*/
|
|
||||||
struct BufferDescriptor
|
|
||||||
{
|
|
||||||
lumiera::StreamType& sType_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ProcNode;
|
class ProcNode;
|
||||||
typedef ProcNode* PNode;
|
typedef ProcNode* PNode;
|
||||||
|
|
||||||
|
|
||||||
struct ChannelDescriptor ///////TODO collapse this with BufferDescriptor?
|
struct ChannelDescriptor ///////TODO really need to define that here? it is needed for node wiring only
|
||||||
{
|
{
|
||||||
BufferDescriptor bufferType;
|
const lumiera::StreamType * bufferType; /////////////////////////////////////////TICKET #828
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InChanDescriptor : ChannelDescriptor
|
struct InChanDescriptor : ChannelDescriptor
|
||||||
|
|
@ -118,5 +104,99 @@ namespace engine {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for a buffer for processing data, abstracting away the actual implementation.
|
||||||
|
* The real buffer pointer can be retrieved by dereferencing this smart-handle class.
|
||||||
|
*
|
||||||
|
* @todo as of 6/2011 it isn't clear how buffer handles are actually created
|
||||||
|
* and how the lifecycle (and memory) management works //////////////////////TICKET #249 rework BuffHandle creation and usage
|
||||||
|
*/
|
||||||
|
class BuffHandle
|
||||||
|
: public lib::BoolCheckable<BuffHandle>
|
||||||
|
{
|
||||||
|
typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
|
||||||
|
|
||||||
|
BufferDescriptor descriptor_;
|
||||||
|
Buff* pBuffer_;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef Buff* PBuff;
|
||||||
|
|
||||||
|
/** @internal a buffer handle may be obtained by "locking"
|
||||||
|
* a buffer from the corresponding BufferProvider */
|
||||||
|
BuffHandle(BufferDescriptor const& typeInfo, PBuff storage = 0)
|
||||||
|
: descriptor_(typeInfo)
|
||||||
|
, pBuffer_(storage)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// using standard copy operations
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void release();
|
||||||
|
|
||||||
|
|
||||||
|
template<typename BU>
|
||||||
|
BU& create();
|
||||||
|
|
||||||
|
template<typename BU>
|
||||||
|
BU& accessAs();
|
||||||
|
|
||||||
|
|
||||||
|
Buff&
|
||||||
|
operator* () const
|
||||||
|
{
|
||||||
|
ENSURE (pBuffer_);
|
||||||
|
return *pBuffer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isValid() const
|
||||||
|
{
|
||||||
|
return bool(pBuffer_)
|
||||||
|
&& descriptor_.verifyValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
size() const
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("forward to the buffer provider for storage size diagnostics");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* === Implementation details === */
|
||||||
|
|
||||||
|
/** convenience shortcut: place and maintain an object within the buffer.
|
||||||
|
* This operation performs the necessary steps to attach an object;
|
||||||
|
* if the buffer isn't locked yet, it will do so. Moreover, the created
|
||||||
|
* object will be owned by the buffer management facilities, i.e. the
|
||||||
|
* destructor is registered as cleanup function.
|
||||||
|
*/
|
||||||
|
template<typename BU>
|
||||||
|
BU&
|
||||||
|
BuffHandle::create()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** convenience shortcut: access the buffer contents in a typesafe fashion.
|
||||||
|
* This is equivalent to a plain dereferentiation with additional metadata check
|
||||||
|
* @throw error::Logic in case of type mismatch \c LUMIERA_ERROR_WRONG_TYPE
|
||||||
|
*/
|
||||||
|
template<typename BU>
|
||||||
|
BU&
|
||||||
|
BuffHandle::accessAs()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("convenience shortcut to access buffer contents typesafe");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace engine
|
} // namespace engine
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
186
src/proc/engine/bufftable-obsolete.hpp
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
BUFFTABLE-OBSOLTE.hpp - Old dead code to be removed when rewriting ProcNode!!!!!
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ENGINE_BUFFHTABLE_OBSOLETE_H
|
||||||
|
#define ENGINE_BUFFHTABLE_OBSOLETE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "proc/engine/buffhandle.hpp"
|
||||||
|
#include "proc/engine/procnode.hpp"
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////WARNING: obsolete code
|
||||||
|
////////////////////////////////WARNING: ...just left in tree to keep it compiling
|
||||||
|
////////////////////////////////TICKET #826 need to be reworked entirely
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
using std::pair;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obsolete, to be rewritten /////TICKET #826
|
||||||
|
*
|
||||||
|
* Tables of buffer handles and corresponding dereferenced buffer pointers.
|
||||||
|
* Used within the invocation of a processing node to calculate data.
|
||||||
|
* The tables are further differentiated into input data buffers and output
|
||||||
|
* data buffers. The tables are supposed to be implemented as bare "C" arrays,
|
||||||
|
* thus the array of real buffer pointers can be fed directly to the
|
||||||
|
* processing function of the respective node.
|
||||||
|
*
|
||||||
|
* @todo this whole design is a first attempt and rather clumsy. It should be reworked
|
||||||
|
* to use a single contiguous memory area and just layer the object structure on top
|
||||||
|
* (by using placement new). Yet the idea of an stack-like organisation should be retained
|
||||||
|
*/
|
||||||
|
struct BuffTable
|
||||||
|
{
|
||||||
|
typedef BuffHandle * PHa;
|
||||||
|
typedef BuffHandle::PBuff * PBu;
|
||||||
|
typedef pair<PHa const,PBu const> Chunk;
|
||||||
|
|
||||||
|
PHa outHandle;
|
||||||
|
PHa inHandle;
|
||||||
|
PBu outBuff;
|
||||||
|
PBu inBuff;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Obsolete, to be rewritten /////TICKET #826 */
|
||||||
|
class BuffTableStorage
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////TICKET #826 need to be reworked entirely
|
||||||
|
/** just a placeholder to decouple the existing code
|
||||||
|
* from the reworked BuffHandle logic. The existing
|
||||||
|
* code in turn will be reworked rather fundamentally
|
||||||
|
*/
|
||||||
|
struct BuffHaXXXX
|
||||||
|
: BuffHandle
|
||||||
|
{
|
||||||
|
BuffHaXXXX() : BuffHandle(just_satisfy_the_compiler()) { /* wont work ever */ }
|
||||||
|
static BufferDescriptor const&
|
||||||
|
just_satisfy_the_compiler() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////TICKET #825 should be backed by mpool and integrated with node invocation
|
||||||
|
vector<BuffHaXXXX> hTab_;
|
||||||
|
vector<BuffHandle::PBuff> pTab_;
|
||||||
|
size_t level_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BuffTableStorage (const size_t maxSiz)
|
||||||
|
: hTab_(maxSiz),
|
||||||
|
pTab_(maxSiz),
|
||||||
|
level_(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
friend class BuffTableChunk;
|
||||||
|
|
||||||
|
/** allocate the given number of slots
|
||||||
|
* starting at current level to be used
|
||||||
|
* by the newly created BuffTableChunk
|
||||||
|
*/
|
||||||
|
BuffTable::Chunk
|
||||||
|
claim (uint slots)
|
||||||
|
{
|
||||||
|
ASSERT (pTab_.size() == hTab_.size());
|
||||||
|
REQUIRE (level_+slots <= hTab_.size());
|
||||||
|
|
||||||
|
size_t prev_level (level_);
|
||||||
|
level_ += slots;
|
||||||
|
return std::make_pair (&hTab_[prev_level],
|
||||||
|
&pTab_[prev_level]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
release (uint slots)
|
||||||
|
{
|
||||||
|
ASSERT (slots <= level_);
|
||||||
|
REQUIRE (level_ <= hTab_.size());
|
||||||
|
REQUIRE (level_ <= pTab_.size());
|
||||||
|
|
||||||
|
level_ -= slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
level_check (BuffTable::Chunk& prev_level)
|
||||||
|
{
|
||||||
|
return prev_level.first == &hTab_[level_]
|
||||||
|
&& prev_level.second == &pTab_[level_];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Obsolete, to be rewritten /////TICKET #826
|
||||||
|
* to be allocated on the stack while evaluating a ProcNode#pull() call.
|
||||||
|
* The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
|
||||||
|
* which can be used to crate such chunks. The claiming and releasing of
|
||||||
|
* slots in the BuffTableStorage is automatically tied to BuffTableChunk
|
||||||
|
* object's lifecycle.
|
||||||
|
*/
|
||||||
|
class BuffTableChunk
|
||||||
|
: public BuffTable,
|
||||||
|
boost::noncopyable
|
||||||
|
{
|
||||||
|
const uint siz_;
|
||||||
|
BuffTable::Chunk tab_;
|
||||||
|
BuffTableStorage& sto_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
|
||||||
|
: siz_(wd.nrI + wd.nrO),
|
||||||
|
tab_(storage.claim (siz_)),
|
||||||
|
sto_(storage)
|
||||||
|
{
|
||||||
|
const uint nrO(wd.nrO);
|
||||||
|
|
||||||
|
// Setup the publicly visible table locations
|
||||||
|
this->outHandle = &tab_.first[ 0 ];
|
||||||
|
this->inHandle = &tab_.first[nrO];
|
||||||
|
this->outBuff = &tab_.second[ 0 ];
|
||||||
|
this->inBuff = &tab_.second[nrO];
|
||||||
|
}
|
||||||
|
|
||||||
|
~BuffTableChunk ()
|
||||||
|
{
|
||||||
|
sto_.release (siz_);
|
||||||
|
ASSERT ( sto_.level_check (tab_),
|
||||||
|
"buffer management logic broken.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
|
#endif
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include "lib/error.hpp"
|
#include "lib/error.hpp"
|
||||||
#include "proc/engine/buffhandle.hpp"
|
#include "proc/engine/buffhandle.hpp"
|
||||||
#include "proc/engine/procnode.hpp"
|
#include "proc/engine/procnode.hpp"
|
||||||
|
#include "lib/iter-adapter.hpp"
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -48,111 +49,106 @@ namespace engine {
|
||||||
* data buffers. The tables are supposed to be implemented as bare "C" arrays,
|
* data buffers. The tables are supposed to be implemented as bare "C" arrays,
|
||||||
* thus the array of real buffer pointers can be fed directly to the
|
* thus the array of real buffer pointers can be fed directly to the
|
||||||
* processing function of the respective node.
|
* processing function of the respective node.
|
||||||
|
*
|
||||||
|
* @todo this whole design is a first attempt and rather clumsy. It should be reworked
|
||||||
|
* to use a single contiguous memory area and just layer the object structure on top
|
||||||
|
* (by using placement new). Yet the idea of an stack-like organisation should be retained
|
||||||
*/
|
*/
|
||||||
struct BuffTable
|
struct BuffTable
|
||||||
{
|
{
|
||||||
typedef BuffHandle * PHa;
|
typedef BuffHandle * PHa;
|
||||||
typedef BuffHandle::PBuff * PBu;
|
typedef BuffHandle::PBuff * PBu;
|
||||||
typedef pair<PHa const,PBu const> Chunk;
|
|
||||||
|
|
||||||
PHa outHandle;
|
struct StorageChunk
|
||||||
PHa inHandle;
|
{ };
|
||||||
PBu outBuff;
|
|
||||||
PBu inBuff;
|
template<uint count>
|
||||||
|
struct Storage
|
||||||
|
{
|
||||||
|
enum{size = count * sizeof(StorageChunk)};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Builder
|
||||||
class BuffTableStorage
|
|
||||||
{
|
{
|
||||||
vector<BuffHandle> hTab_;
|
|
||||||
vector<BuffHandle::PBuff> pTab_;
|
|
||||||
size_t level_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BuffTableStorage (const size_t maxSiz)
|
Builder& announce (uint count, BufferDescriptor const& type);
|
||||||
: hTab_(maxSiz),
|
BuffTable& build();
|
||||||
pTab_(maxSiz),
|
};
|
||||||
level_(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); }
|
static Builder& prepare (const size_t STORAGE_SIZE, void* storage);
|
||||||
|
|
||||||
protected:
|
void lockBuffers();
|
||||||
|
void releaseBuffers();
|
||||||
|
|
||||||
friend class BuffTableChunk;
|
typedef vector<BuffHandle> BuffHandleTable;
|
||||||
|
typedef lib::RangeIter<BuffHandleTable::iterator> iterator;
|
||||||
|
|
||||||
/** allocate the given number of slots
|
iterator buffers();
|
||||||
* starting at current level to be used
|
iterator inBuffers();
|
||||||
* by the newly created BuffTableChunk
|
iterator outBuffers();
|
||||||
*/
|
|
||||||
BuffTable::Chunk
|
|
||||||
claim (uint slots)
|
|
||||||
{
|
|
||||||
ASSERT (pTab_.size() == hTab_.size());
|
|
||||||
REQUIRE (level_+slots <= hTab_.size());
|
|
||||||
|
|
||||||
size_t prev_level (level_);
|
|
||||||
level_ += slots;
|
|
||||||
return std::make_pair (&hTab_[prev_level],
|
|
||||||
&pTab_[prev_level]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
release (uint slots)
|
|
||||||
{
|
|
||||||
ASSERT (slots <= level_);
|
|
||||||
REQUIRE (level_ <= hTab_.size());
|
|
||||||
REQUIRE (level_ <= pTab_.size());
|
|
||||||
|
|
||||||
level_ -= slots;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
level_check (BuffTable::Chunk& prev_level)
|
|
||||||
{
|
|
||||||
return prev_level.first == &hTab_[level_]
|
|
||||||
&& prev_level.second == &pTab_[level_];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* to be allocated on the stack while evaluating a ProcNode#pull() call.
|
|
||||||
* The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
|
|
||||||
* which can be used to crate such chunks. The claiming and releasing of
|
|
||||||
* slots in the BuffTableStorage is automatically tied to BuffTableChunk
|
|
||||||
* object's lifecycle.
|
|
||||||
*/
|
|
||||||
class BuffTableChunk
|
|
||||||
: public BuffTable,
|
|
||||||
boost::noncopyable
|
|
||||||
{
|
|
||||||
const uint siz_;
|
|
||||||
BuffTable::Chunk tab_;
|
|
||||||
BuffTableStorage& sto_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
|
|
||||||
: siz_(wd.nrI + wd.nrO),
|
|
||||||
tab_(storage.claim (siz_)),
|
|
||||||
sto_(storage)
|
|
||||||
{
|
|
||||||
const uint nrO(wd.nrO);
|
|
||||||
|
|
||||||
// Setup the public visible table locations
|
/* === Implementation === */
|
||||||
this->outHandle = &tab_.first[ 0 ];
|
|
||||||
this->inHandle = &tab_.first[nrO];
|
inline BuffTable::Builder&
|
||||||
this->outBuff = &tab_.second[ 0 ];
|
BuffTable::prepare (const size_t STORAGE_SIZE, void* storage)
|
||||||
this->inBuff = &tab_.second[nrO];
|
{
|
||||||
|
UNIMPLEMENTED ("expose a builder object for outfitting a buffer pointer table");
|
||||||
}
|
}
|
||||||
|
|
||||||
~BuffTableChunk ()
|
|
||||||
|
inline BuffTable::Builder&
|
||||||
|
BuffTable::Builder::announce (uint count, BufferDescriptor const& type)
|
||||||
{
|
{
|
||||||
sto_.release (siz_);
|
UNIMPLEMENTED ("accept announcement of additional buffer table entries required");
|
||||||
ASSERT ( sto_.level_check (tab_),
|
|
||||||
"buffer management logic broken.");
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
inline BuffTable&
|
||||||
|
BuffTable::Builder::build()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("finally drop off the newly configured buffer pointer table");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void
|
||||||
|
BuffTable::lockBuffers()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("convenience shortcut: lock all preconfigured buffers within this table through the underlying buffer provider");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void
|
||||||
|
BuffTable::releaseBuffers()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("convenience shortcut: release all the buffers managed through this buffer table, by forwarding to the underlying buffer provider");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline BuffTable::iterator
|
||||||
|
BuffTable::buffers()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("expose an iterator to yield all prepared buffers within this buffer table");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline BuffTable::iterator
|
||||||
|
BuffTable::inBuffers()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("expose an iterator to access all the input buffer slots of this buffer table");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline BuffTable::iterator
|
||||||
|
BuffTable::outBuffers()
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("expose an iterator to access all the output buffer slots of this buffer table");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
94
src/proc/engine/calc-stream.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
CALC-STREAM.hpp - abstraction representing a series of scheduled calculations
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PROC_ENGINE_CALC_STREAM_H
|
||||||
|
#define PROC_ENGINE_CALC_STREAM_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
//#include "include/dummy-player-facade.h"
|
||||||
|
//#include "include/display-facade.h"
|
||||||
|
//#include "common/instancehandle.hpp"
|
||||||
|
//#include "lib/singleton-ref.hpp"
|
||||||
|
//
|
||||||
|
//#include <boost/noncopyable.hpp>
|
||||||
|
//#include <boost/scoped_ptr.hpp>
|
||||||
|
//#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace proc {
|
||||||
|
namespace engine{
|
||||||
|
|
||||||
|
// using std::string;
|
||||||
|
// using lumiera::Subsys;
|
||||||
|
// using lumiera::Display;
|
||||||
|
// using lumiera::DummyPlayer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* A calculation stream groups and abstracts a series of
|
||||||
|
* calculation jobs, delivering frames into the configured
|
||||||
|
* OutputSlot in a timely fashion. Behind the scenes, this
|
||||||
|
* gets translated into several jobs enqueued with the
|
||||||
|
* scheduler in the backend layer. The calculation stream
|
||||||
|
* implementation cares to create and configure these
|
||||||
|
* jobs and to manage the necessary dependencies and
|
||||||
|
* callbacks.
|
||||||
|
*
|
||||||
|
* Regarding the implementation, a CalcStream is an const
|
||||||
|
* value object holding the metadata necessary to manage
|
||||||
|
* the underlying jobs. The only way to create a CalcStream
|
||||||
|
* properly is to retrieve it from the factory functions
|
||||||
|
* of the EngineService. At that point, the corresponding
|
||||||
|
* jobs will be configured and enqueued.
|
||||||
|
*/
|
||||||
|
class CalcStream
|
||||||
|
{
|
||||||
|
|
||||||
|
friend class EngineService;
|
||||||
|
|
||||||
|
CalcStream()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CalcStream (CalcStream const& o)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED("build a calculation stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
~CalcStream() { }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
|
} // namespace proc
|
||||||
|
#endif
|
||||||
220
src/proc/engine/diagnostic-buffer-provider.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
DiagnosticBufferProvider - helper for testing against the BufferProvider interface
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
* *****************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "include/logging.h"
|
||||||
|
#include "lib/meta/function.hpp"
|
||||||
|
#include "lib/scoped-ptrvect.hpp"
|
||||||
|
|
||||||
|
#include "proc/engine/diagnostic-buffer-provider.hpp"
|
||||||
|
|
||||||
|
#include <boost/scoped_array.hpp>
|
||||||
|
//#include <vector>
|
||||||
|
|
||||||
|
using lib::ScopedPtrVect;
|
||||||
|
using boost::scoped_array;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
|
||||||
|
/** Storage for the diagnostics frontend */
|
||||||
|
lib::Singleton<DiagnosticBufferProvider> DiagnosticBufferProvider::diagnostics;
|
||||||
|
|
||||||
|
|
||||||
|
class Block
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
size_t size_;
|
||||||
|
scoped_array<char> storage_;
|
||||||
|
|
||||||
|
bool was_locked_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Block()
|
||||||
|
: size_(0)
|
||||||
|
, storage_()
|
||||||
|
, was_locked_(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool
|
||||||
|
was_used() const
|
||||||
|
{
|
||||||
|
return was_locked_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
was_closed() const
|
||||||
|
{
|
||||||
|
return was_locked_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
accessMemory() const
|
||||||
|
{
|
||||||
|
return storage_.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace { // Details of allocation and accounting
|
||||||
|
|
||||||
|
const uint MAX_BUFFERS = 50;
|
||||||
|
|
||||||
|
} // (END) Details of allocation and accounting
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal DiagnosticBufferProvider's PImpl.
|
||||||
|
* Uses a linearly growing table of heap allocated buffer blocks,
|
||||||
|
* which will never be discarded, unless the PImpl is discarded as a whole.
|
||||||
|
* This way, the tracked usage information remains available after the fact.
|
||||||
|
*/
|
||||||
|
class DiagnosticBufferProvider::HeapMemProvider
|
||||||
|
: public BufferProvider
|
||||||
|
, public ScopedPtrVect<Block>
|
||||||
|
{
|
||||||
|
|
||||||
|
virtual uint
|
||||||
|
announce (uint count, BufferDescriptor const& type)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("pre-register storage for buffers of a specific kind");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual BuffHandle
|
||||||
|
lockBufferFor (BufferDescriptor const& descriptor)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("lock buffer for exclusive use");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
releaseBuffer (BuffHandle const& handle)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("release a buffer and invalidate the handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
HeapMemProvider()
|
||||||
|
: BufferProvider ("Diagnostic_HeapAllocated")
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual ~HeapMemProvider()
|
||||||
|
{
|
||||||
|
INFO (proc_mem, "discarding %zu diagnostic buffer entries", HeapMemProvider::size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Block&
|
||||||
|
access_or_create (uint bufferID)
|
||||||
|
{
|
||||||
|
while (!withinStorageSize (bufferID))
|
||||||
|
manage (new Block);
|
||||||
|
|
||||||
|
ENSURE (withinStorageSize (bufferID));
|
||||||
|
return (*this)[bufferID];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool
|
||||||
|
withinStorageSize (uint bufferID) const
|
||||||
|
{
|
||||||
|
if (bufferID >= MAX_BUFFERS)
|
||||||
|
throw error::Fatal ("hardwired internal limit for test buffers exceeded");
|
||||||
|
|
||||||
|
return bufferID < size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DiagnosticBufferProvider::DiagnosticBufferProvider()
|
||||||
|
: pImpl_() //////////TODO create PImpl here
|
||||||
|
{ }
|
||||||
|
|
||||||
|
DiagnosticBufferProvider::~DiagnosticBufferProvider() { }
|
||||||
|
|
||||||
|
|
||||||
|
BufferProvider&
|
||||||
|
DiagnosticBufferProvider::build()
|
||||||
|
{
|
||||||
|
return diagnostics().reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DiagnosticBufferProvider&
|
||||||
|
DiagnosticBufferProvider::access (BufferProvider const& provider)
|
||||||
|
{
|
||||||
|
if (!diagnostics().isCurrent (provider))
|
||||||
|
throw error::Invalid("given Provider doesn't match (current) diagnostic data record."
|
||||||
|
"This might be an lifecycle error. Did you build() this instance beforehand?");
|
||||||
|
|
||||||
|
return diagnostics();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DiagnosticBufferProvider::HeapMemProvider&
|
||||||
|
DiagnosticBufferProvider::reset()
|
||||||
|
{
|
||||||
|
pImpl_.reset(new HeapMemProvider());
|
||||||
|
return *pImpl_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
DiagnosticBufferProvider::isCurrent (BufferProvider const& implInstance)
|
||||||
|
{
|
||||||
|
return &implInstance == pImpl_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === diagnostic API === */
|
||||||
|
|
||||||
|
bool
|
||||||
|
DiagnosticBufferProvider::buffer_was_used (uint bufferID) const
|
||||||
|
{
|
||||||
|
return pImpl_->access_or_create(bufferID).was_used();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
DiagnosticBufferProvider::buffer_was_closed (uint bufferID) const
|
||||||
|
{
|
||||||
|
return pImpl_->access_or_create(bufferID).was_closed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void*
|
||||||
|
DiagnosticBufferProvider::accessMemory (uint bufferID) const
|
||||||
|
{
|
||||||
|
return pImpl_->access_or_create(bufferID).accessMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
125
src/proc/engine/diagnostic-buffer-provider.hpp
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
DIAGNOSTIC-BUFFER-PROVIDER.hpp - helper for testing against the BufferProvider interface
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file diagnostic-buffer-provider.hpp
|
||||||
|
** An facility for writing unit-tests targetting the BufferProvider interface.
|
||||||
|
**
|
||||||
|
** @see buffer-provider-protocol-test.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H
|
||||||
|
#define PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "lib/singleton.hpp"
|
||||||
|
#include "lib/util.hpp"
|
||||||
|
#include "proc/engine/buffer-provider.hpp"
|
||||||
|
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
namespace error = lumiera::error;
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Helper for unit tests: Buffer provider reference implementation.
|
||||||
|
*
|
||||||
|
* @todo write type comment
|
||||||
|
*/
|
||||||
|
class DiagnosticBufferProvider
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* simple BufferProvider implementation
|
||||||
|
* with additional allocation tracking
|
||||||
|
*/
|
||||||
|
class HeapMemProvider;
|
||||||
|
|
||||||
|
|
||||||
|
boost::scoped_ptr<HeapMemProvider> pImpl_;
|
||||||
|
static lib::Singleton<DiagnosticBufferProvider> diagnostics;
|
||||||
|
|
||||||
|
|
||||||
|
HeapMemProvider& reset();
|
||||||
|
bool isCurrent (BufferProvider const&);
|
||||||
|
|
||||||
|
|
||||||
|
DiagnosticBufferProvider();
|
||||||
|
~DiagnosticBufferProvider();
|
||||||
|
|
||||||
|
friend class lib::singleton::StaticCreate<DiagnosticBufferProvider>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** build a new Diagnostic Buffer Provider instance,
|
||||||
|
* discard the existing one. Use the static query API
|
||||||
|
* for investigating collected data. */
|
||||||
|
static BufferProvider& build();
|
||||||
|
|
||||||
|
|
||||||
|
/** access the diagnostic API of the buffer provider
|
||||||
|
* @throw error::Invalid if the given provider doesn't allow
|
||||||
|
* for diagnostic access or wasn't registered beforehand.
|
||||||
|
*/
|
||||||
|
static DiagnosticBufferProvider&
|
||||||
|
access (BufferProvider const& );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* === diagnostic API === */
|
||||||
|
|
||||||
|
bool buffer_was_used (uint bufferID) const;
|
||||||
|
bool buffer_was_closed (uint bufferID) const;
|
||||||
|
void* accessMemory (uint bufferID) const;
|
||||||
|
bool all_buffers_released() const;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename BU>
|
||||||
|
bool
|
||||||
|
object_was_attached (uint bufferID) const
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("verify object attachment status of a specific buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename BU>
|
||||||
|
bool
|
||||||
|
object_was_destroyed (uint bufferID) const
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("verify object attachment status of a specific buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
|
#endif
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
BuffHandle - Buffer handling support for the render engine
|
Dispatcher - translating calculation streams into frame jobs
|
||||||
|
|
||||||
Copyright (C) Lumiera.org
|
Copyright (C) Lumiera.org
|
||||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU General Public License as
|
modify it under the terms of the GNU General Public License as
|
||||||
|
|
@ -21,12 +21,17 @@
|
||||||
* *****************************************************/
|
* *****************************************************/
|
||||||
|
|
||||||
|
|
||||||
#include "proc/engine/buffhandle.hpp"
|
#include "proc/engine/dispatcher.hpp"
|
||||||
|
//#include "lib/frameid.hpp"
|
||||||
|
//#include "proc/state.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace engine {
|
namespace engine {
|
||||||
|
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace engine
|
} // namespace engine
|
||||||
62
src/proc/engine/dispatcher.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
DISPATCHER.hpp - translating calculation streams into frame jobs
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PROC_ENGINE_DISPATCHER_H
|
||||||
|
#define PROC_ENGINE_DISPATCHER_H
|
||||||
|
|
||||||
|
#include "proc/common.hpp"
|
||||||
|
#include "proc/state.hpp"
|
||||||
|
#include "lib/time/timevalue.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
using lib::time::TimeSpan;
|
||||||
|
using lib::time::FSecs;
|
||||||
|
using lib::time::Time;
|
||||||
|
//
|
||||||
|
// class ExitNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo
|
||||||
|
*/
|
||||||
|
class Dispatcher
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
/** timerange covered by this RenderGraph */
|
||||||
|
TimeSpan segment_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Dispatcher()
|
||||||
|
: segment_(Time::ZERO, FSecs(5))
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("anything regarding the Engine backbone");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
|
#endif
|
||||||
113
src/proc/engine/engine-diagnostics.hpp
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
ENGINE-DIAGNOSTICS.hpp - diagnostic facility to investigate engine operation
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file engine-diagnostics.hpp
|
||||||
|
** An facility to check and monitor engine operations.
|
||||||
|
** Once created, an EngineDiagnostics object connects to the EngineService
|
||||||
|
** interface to activate additional tracing facilities within the engine,
|
||||||
|
** allowing to watch and verify the creation of individual jobs and further
|
||||||
|
** engine state parameters.
|
||||||
|
**
|
||||||
|
** @see EngineInterface
|
||||||
|
** @see engine-interface-test.cpp
|
||||||
|
** @see calc-stream-test.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PROC_ENGINE_ENGINE_DIAGNOSTICS_H
|
||||||
|
#define PROC_ENGINE_ENGINE_DIAGNOSTICS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "proc/engine/engine-service.hpp"
|
||||||
|
//#include "include/dummy-player-facade.h"
|
||||||
|
//#include "include/display-facade.h"
|
||||||
|
//#include "proc/engine/calc-stream.hpp"
|
||||||
|
//#include "proc/mobject/model-port.hpp"
|
||||||
|
#include "proc/play/timings.hpp"
|
||||||
|
//#include "proc/play/output-slot.hpp"
|
||||||
|
//#include "common/instancehandle.hpp"
|
||||||
|
//#include "lib/singleton-ref.hpp"
|
||||||
|
//#include "lib/polymorphic-value.hpp"
|
||||||
|
//#include "lib/singleton.hpp"
|
||||||
|
//
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
//#include <boost/scoped_ptr.hpp>
|
||||||
|
//#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace proc {
|
||||||
|
namespace engine{
|
||||||
|
|
||||||
|
// using std::string;
|
||||||
|
// using lumiera::Subsys;
|
||||||
|
// using lumiera::Display;
|
||||||
|
// using lumiera::DummyPlayer;
|
||||||
|
using proc::play::Timings;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************
|
||||||
|
* Render engine diagnostic facility. Creating an instance
|
||||||
|
* will activate additional tracing facilities within the
|
||||||
|
* render engine; results may be investigated through
|
||||||
|
* EngineDiagnostics public functions. The object acts
|
||||||
|
* like a smart handle, i.e. the tracing facilities will
|
||||||
|
* be disabled and disconnected when going out of scope.
|
||||||
|
*/
|
||||||
|
class EngineDiagnostics
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
EngineService& engine_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
EngineDiagnostics (EngineService& e)
|
||||||
|
: engine_(e)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("attach tracing connector");
|
||||||
|
engine_.activateTracing();
|
||||||
|
}
|
||||||
|
|
||||||
|
~EngineDiagnostics()
|
||||||
|
{
|
||||||
|
TODO ("detach tracing connector");
|
||||||
|
engine_.disableTracing();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** */
|
||||||
|
bool
|
||||||
|
has_scheduled_jobs_for (Timings const& timings)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("Engine Diagnostics: query scheduled jobs");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
|
} // namespace proc
|
||||||
|
#endif
|
||||||
135
src/proc/engine/engine-service.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
EngineService - primary service access point for using the renderengine
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
* *****************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "proc/engine/engine-service.hpp"
|
||||||
|
|
||||||
|
//#include <string>
|
||||||
|
//#include <memory>
|
||||||
|
//#include <tr1/functional>
|
||||||
|
//#include <boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace proc {
|
||||||
|
namespace engine{
|
||||||
|
|
||||||
|
// using std::string;
|
||||||
|
// using lumiera::Subsys;
|
||||||
|
// using std::auto_ptr;
|
||||||
|
// using boost::scoped_ptr;
|
||||||
|
// using std::tr1::bind;
|
||||||
|
|
||||||
|
|
||||||
|
namespace { // hidden local details of the service implementation....
|
||||||
|
|
||||||
|
} // (End) hidden service impl details
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** storage for the EngineService interface object */
|
||||||
|
lib::Singleton<EngineService> EngineService::instance;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** */
|
||||||
|
EngineService::EngineService()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** */
|
||||||
|
CalcStream
|
||||||
|
EngineService::calculate(ModelPort mPort,
|
||||||
|
Timings nominalTimings,
|
||||||
|
OutputConnection output,
|
||||||
|
Quality serviceQuality)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("build a standard calculation stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** */
|
||||||
|
CalcStream
|
||||||
|
EngineService::calculateBackground(ModelPort mPort,
|
||||||
|
Timings nominalTimings,
|
||||||
|
Quality serviceQuality)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED ("build a calculation stream for background rendering");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== Quality-of-Service ===== */
|
||||||
|
|
||||||
|
|
||||||
|
EngineService::Quality::~Quality() { } // emit vtables here...
|
||||||
|
|
||||||
|
enum CalcType {
|
||||||
|
PLAYBACK,
|
||||||
|
RENDER,
|
||||||
|
BACKGROUND
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultQoS
|
||||||
|
: public EngineService::Quality
|
||||||
|
{
|
||||||
|
CalcType type_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DefaultQoS (CalcType type)
|
||||||
|
: type_(type)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class PriorityQoS
|
||||||
|
: public DefaultQoS
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
PriorityQoS ()
|
||||||
|
: DefaultQoS(PLAYBACK)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Compromise
|
||||||
|
: public DefaultQoS
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
Compromise (CalcType type)
|
||||||
|
: DefaultQoS(type)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EngineService::QoS_Definition EngineService::QoS_DEFAULT = QoS_Definition::build<DefaultQoS> (PLAYBACK);
|
||||||
|
EngineService::QoS_Definition EngineService::QoS_BACKGROUND = QoS_Definition::build<DefaultQoS> (BACKGROUND);
|
||||||
|
EngineService::QoS_Definition EngineService::QoS_COMPROMISE = QoS_Definition::build<Compromise> (PLAYBACK);
|
||||||
|
EngineService::QoS_Definition EngineService::QoS_PERFECT_RESULT = QoS_Definition::build<DefaultQoS> (RENDER);
|
||||||
|
EngineService::QoS_Definition EngineService::QoS_SYNC_PRIORITY = QoS_Definition::build<PriorityQoS>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}} // namespace proc::engine
|
||||||
167
src/proc/engine/engine-service.hpp
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
ENGINE-SERVICE.hpp - primary service access point for using the renderengine
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file engine-service.hpp
|
||||||
|
** Access point for the (core) calculation service of the render engine.
|
||||||
|
** This Proc-Layer internal service is provided for use by the Player subsystem.
|
||||||
|
** The actual implementation is forwarded to backend services (especially the scheduler).
|
||||||
|
** The EngineService singleton has no state beyond the jobs currently managed by the
|
||||||
|
** scheduler; when the latter isn't available, any invocation will throw.
|
||||||
|
**
|
||||||
|
** The central concept provided through this facade interface is the <i>calculation stream</i>.
|
||||||
|
** This represents a series of calculations, expected to happen in a timely fashion and in order
|
||||||
|
** to deliver a frame data stream onto an opened output connection. On the implementation side,
|
||||||
|
** a calculation stream will be translated into a series of jobs invoking render nodes,
|
||||||
|
** to be executed through the scheduler in the backend layer.
|
||||||
|
**
|
||||||
|
** While the individual CalcStram is simple, linear and unmodifiable, any CalcStream may be
|
||||||
|
** \em superseded by a new definition. In this case, the engine will care for a seamless
|
||||||
|
** switch and continuation; under the hood, there is a mechanism to discard resources
|
||||||
|
** tied to the original CalcStream, once the switch to the new definition is complete.
|
||||||
|
**
|
||||||
|
** @see EngineInterface_test
|
||||||
|
** @see CalcStream_test
|
||||||
|
** @see proc::play::PlayerService
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PROC_ENGINE_ENGINE_SERVICE_H
|
||||||
|
#define PROC_ENGINE_ENGINE_SERVICE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
//#include "include/dummy-player-facade.h"
|
||||||
|
//#include "include/display-facade.h"
|
||||||
|
#include "proc/engine/calc-stream.hpp"
|
||||||
|
#include "proc/mobject/model-port.hpp"
|
||||||
|
#include "proc/play/timings.hpp"
|
||||||
|
#include "proc/play/output-slot.hpp"
|
||||||
|
//#include "common/instancehandle.hpp"
|
||||||
|
//#include "lib/singleton-ref.hpp"
|
||||||
|
#include "lib/polymorphic-value.hpp"
|
||||||
|
#include "lib/singleton.hpp"
|
||||||
|
//
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
//#include <boost/scoped_ptr.hpp>
|
||||||
|
//#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace proc {
|
||||||
|
namespace engine{
|
||||||
|
|
||||||
|
// using std::string;
|
||||||
|
// using lumiera::Subsys;
|
||||||
|
// using lumiera::Display;
|
||||||
|
// using lumiera::DummyPlayer;
|
||||||
|
using mobject::ModelPort;
|
||||||
|
using proc::play::Timings;
|
||||||
|
|
||||||
|
typedef proc::play::OutputSlot::Allocation OutputConnection;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************
|
||||||
|
* A service to schedule series of calculations,
|
||||||
|
* delivering the rendered data into an external output
|
||||||
|
* sink in a timely fashion. Actually the CalculationStream
|
||||||
|
* instances provided through this (facade) interface are
|
||||||
|
* backed by jobs executed through the scheduler in the
|
||||||
|
* backend layer. The implementation of this service
|
||||||
|
* cares to create the right job entries in the correct
|
||||||
|
* order and to enqueue these into the scheduler.
|
||||||
|
*/
|
||||||
|
class EngineService
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/* The following typedefs allow to hand out predefined
|
||||||
|
* Quality-of-Service strategy definitions as value objects,
|
||||||
|
* without disclosing implementation details here in this header.
|
||||||
|
*/
|
||||||
|
enum{ QoS_IMPL_SIZE = sizeof(size_t) }; /////////////////////////////////////////TODO is this correct??
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*************************************************************
|
||||||
|
* Quality-of-Service definition for an Render Engine usage.
|
||||||
|
* This strategy defines how to decide between conflicting goals like
|
||||||
|
* - timely delivery
|
||||||
|
* - image quality
|
||||||
|
* - niceness and resource usage
|
||||||
|
*/
|
||||||
|
class Quality
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Quality(); ///< this is an Interface
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef lib::polyvalue::CloneValueSupport<Quality> _Clonable_QoS_Strategy;
|
||||||
|
typedef lib::PolymorphicValue<Quality, QoS_IMPL_SIZE, _Clonable_QoS_Strategy> QoS_Definition;
|
||||||
|
|
||||||
|
static QoS_Definition QoS_DEFAULT;
|
||||||
|
static QoS_Definition QoS_BACKGROUND;
|
||||||
|
static QoS_Definition QoS_SYNC_PRIORITY;
|
||||||
|
static QoS_Definition QoS_PERFECT_RESULT;
|
||||||
|
static QoS_Definition QoS_COMPROMISE;
|
||||||
|
|
||||||
|
|
||||||
|
/** access point to the Engine Interface.
|
||||||
|
* @internal this is an facade interface for internal use
|
||||||
|
* by the player. Client code should use the Player.
|
||||||
|
*/
|
||||||
|
static lib::Singleton<EngineService> instance;
|
||||||
|
|
||||||
|
|
||||||
|
EngineService();
|
||||||
|
~EngineService() { }
|
||||||
|
|
||||||
|
CalcStream
|
||||||
|
calculate(ModelPort mPort,
|
||||||
|
Timings nominalTimings,
|
||||||
|
OutputConnection output,
|
||||||
|
Quality serviceQuality =QoS_DEFAULT);
|
||||||
|
|
||||||
|
CalcStream
|
||||||
|
calculateBackground(ModelPort mPort,
|
||||||
|
Timings nominalTimings,
|
||||||
|
Quality serviceQuality =QoS_BACKGROUND);
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void activateTracing();
|
||||||
|
void disableTracing(); ///< EX_FREE
|
||||||
|
|
||||||
|
friend class EngineDiagnostics;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace engine
|
||||||
|
} // namespace proc
|
||||||
|
#endif
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
#ifndef ENGINE_MASK_H
|
#ifndef ENGINE_MASK_H
|
||||||
#define ENGINE_MASK_H
|
#define ENGINE_MASK_H
|
||||||
|
|
||||||
#include "proc/engine/trafo.hpp"
|
#include "proc/engine/procnode.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace engine
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
class Mask : public Trafo
|
class Mask : public ProcNode
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
#include "proc/state.hpp"
|
#include "proc/state.hpp"
|
||||||
#include "proc/engine/procnode.hpp"
|
#include "proc/engine/procnode.hpp"
|
||||||
#include "proc/engine/buffhandle.hpp"
|
#include "proc/engine/buffhandle.hpp"
|
||||||
#include "proc/engine/bufftable.hpp"
|
#include "proc/engine/bufftable-obsolete.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -172,6 +172,7 @@ namespace engine {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////TICKET #249 this strategy should better be hidden within the BuffHandle ctor (and type-erased after creation)
|
||||||
struct AllocBufferFromParent ///< using the parent StateAdapter for buffer allocations
|
struct AllocBufferFromParent ///< using the parent StateAdapter for buffer allocations
|
||||||
: Invocation
|
: Invocation
|
||||||
{
|
{
|
||||||
|
|
@ -179,7 +180,7 @@ namespace engine {
|
||||||
: Invocation(sta, w, outCh) {}
|
: Invocation(sta, w, outCh) {}
|
||||||
|
|
||||||
virtual BuffHandle
|
virtual BuffHandle
|
||||||
allocateBuffer (BufferDescriptor const& bd) { return parent_.allocateBuffer(bd); } ////////////TODO: actually implement the "allocate from parent" logic!
|
allocateBuffer (const lumiera::StreamType* ty) { return parent_.allocateBuffer(ty); } ////////////TODO: actually implement the "allocate from parent" logic!
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AllocBufferFromCache ///< using the global current State, which will delegate to Cache
|
struct AllocBufferFromCache ///< using the global current State, which will delegate to Cache
|
||||||
|
|
@ -189,7 +190,7 @@ namespace engine {
|
||||||
: Invocation(sta, w, outCh) {}
|
: Invocation(sta, w, outCh) {}
|
||||||
|
|
||||||
virtual BuffHandle
|
virtual BuffHandle
|
||||||
allocateBuffer (BufferDescriptor const& bd) { return current_.allocateBuffer(bd); }
|
allocateBuffer (const lumiera::StreamType* ty) { return current_.allocateBuffer(ty); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
#include "proc/state.hpp"
|
#include "proc/state.hpp"
|
||||||
#include "proc/engine/procnode.hpp"
|
#include "proc/engine/procnode.hpp"
|
||||||
#include "proc/engine/buffhandle.hpp"
|
#include "proc/engine/buffhandle.hpp"
|
||||||
#include "proc/engine/bufftable.hpp"
|
#include "proc/engine/bufftable-obsolete.hpp"
|
||||||
#include "proc/engine/nodeinvocation.hpp"
|
#include "proc/engine/nodeinvocation.hpp"
|
||||||
|
|
||||||
#include "lib/meta/util.hpp"
|
#include "lib/meta/util.hpp"
|
||||||
|
|
@ -245,7 +245,7 @@ namespace config {
|
||||||
|
|
||||||
template<class NEXT>
|
template<class NEXT>
|
||||||
struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor??
|
struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor??
|
||||||
{ ///////////////// this would require BuffHandle to be a smart ref....
|
{ ///////////////// this would require BuffHandle to be a smart ref.... --> ///TICKET #249
|
||||||
BuffHandle
|
BuffHandle
|
||||||
step (Invocation& ivo)
|
step (Invocation& ivo)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
#include "proc/engine/nodeoperation.hpp"
|
#include "proc/engine/nodeoperation.hpp"
|
||||||
#include "proc/engine/nodewiring-config.hpp"
|
#include "proc/engine/nodewiring-config.hpp"
|
||||||
|
|
||||||
#include "lib/meta/typelist-util.hpp"
|
#include "lib/meta/typelist-manip.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace engine {
|
namespace engine {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
#ifndef ENGINE_PLUGINADAPTER_H
|
#ifndef ENGINE_PLUGINADAPTER_H
|
||||||
#define ENGINE_PLUGINADAPTER_H
|
#define ENGINE_PLUGINADAPTER_H
|
||||||
|
|
||||||
#include "proc/engine/trafo.hpp"
|
#include "proc/engine/procnode.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace engine
|
||||||
* Effects processors are typically defined in a separate library and
|
* Effects processors are typically defined in a separate library and
|
||||||
* will be loaded at runtime using Lumiera's plugin interface.
|
* will be loaded at runtime using Lumiera's plugin interface.
|
||||||
*/
|
*/
|
||||||
class PluginAdapter : public Trafo
|
class PluginAdapter : public ProcNode
|
||||||
{
|
{
|
||||||
/////////////
|
/////////////
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
//#include "proc/state.hpp"
|
//#include "proc/state.hpp"
|
||||||
#include "proc/engine/procnode.hpp"
|
#include "proc/engine/procnode.hpp"
|
||||||
#include "proc/engine/buffhandle.hpp"
|
#include "proc/engine/buffhandle.hpp"
|
||||||
//#include "proc/engine/bufftable.hpp"
|
//#include "proc/engine/bufftable-obsolete.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,11 @@
|
||||||
|
|
||||||
using std::list;
|
using std::list;
|
||||||
|
|
||||||
|
/////////////////////////////TODO 7/11 this is a piece of debris, left over from the first attempt to complete the render nodes network.
|
||||||
|
/////////////////////////////TODO Meanwhile the intention is to treat the render nodes network more like a data structure,
|
||||||
|
/////////////////////////////TODO consequently this will become some kind of root or anchor point for this network
|
||||||
|
|
||||||
|
//////////TODO for the "real" engine API: look at engine-serivce.hpp
|
||||||
|
|
||||||
namespace engine {
|
namespace engine {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
#include "lib/time/timevalue.hpp"
|
#include "lib/time/timevalue.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////TODO 7/11 this is a piece of debris, left over from the first attempt to complete the render nodes network.
|
||||||
|
/////////////////////////////TODO Meanwhile the intention is to treat the render nodes network more like a data structure,
|
||||||
|
/////////////////////////////TODO consequently this will become some kind of root or anchor point for this network
|
||||||
|
|
||||||
|
|
||||||
namespace engine {
|
namespace engine {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ namespace engine {
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
BuffHandle
|
BuffHandle
|
||||||
StateProxy::allocateBuffer (BufferDescriptor const&)
|
StateProxy::allocateBuffer (const lumiera::StreamType*)
|
||||||
{
|
{
|
||||||
UNIMPLEMENTED ("allocate a suitable buffer to hold a frame of the denoted type");
|
UNIMPLEMENTED ("allocate a suitable buffer to hold a frame of the denoted type");
|
||||||
}
|
}
|
||||||
|
|
|
||||||