sync documentation tree with current master

This commit is contained in:
Fischlurch 2011-10-21 02:06:41 +02:00
commit da6860f6a5
180 changed files with 11600 additions and 1683 deletions

View file

@ -254,8 +254,10 @@ def configurePlatform(env):
problems.append('We need boost::format (header).')
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).')
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++'):
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++'):
problems.append('We need the boost regular expression lib (incl. binary lib for linking).')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

BIN
doc/devel/uml/fig143877.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
doc/devel/uml/fig144005.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
doc/devel/uml/fig145157.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,8 +1,8 @@
/*
Trafo - transforming processing Node
SchedulerFrontend - access point to the scheduler within the renderengine
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
modify it under the terms of the GNU General Public License as
@ -21,13 +21,14 @@
* *****************************************************/
#include "proc/engine/trafo.hpp"
namespace engine
{
#include "backend/engine/scheduler-frontend.hpp"
namespace backend{
namespace engine {
/** */
} // namespace engine
}} // namespace backend::engine

View 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

View file

@ -200,7 +200,7 @@ namespace backend {
/** 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.
*/
void

View file

@ -156,7 +156,7 @@ namespace gui {
if (facade)
{
WARN (guifacade, "GUI subsystem terminates, but GuiFacade isn't properly closed. "
"Closing it forcedly; this indicates broken startup logic and should be fixed.");
"Closing it forcedly; this indicates broken startup logic and should be fixed.");
try { facade.reset (0); }
catch(...) { WARN_IF (lumiera_error_peek(), guifacade, "Ignoring error: %s", lumiera_error()); }
lumiera_error(); // clear any remaining error state...

View file

@ -184,7 +184,7 @@ namespace lumiera {
public:
/** Set up an InstanceHandle representing a plugin.
* Should be placed at the client side.
* Should be placed at the client side.
* @param iName unmangled name of the interface
* @param version major version
* @param minminor minimum acceptable minor version number
@ -203,7 +203,7 @@ namespace lumiera {
* registration and deregistration of interface(s).
* Should be placed at the service providing side.
* @param a (single) interface descriptor, which can be created with
* LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF
* LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF
*/
InstanceHandle (LumieraInterface descriptor)
: desc_(descriptor)
@ -222,9 +222,9 @@ namespace lumiera {
/** act as smart pointer providing access through the facade.
/** act as smart pointer providing access through the facade.
* @note we don't provide operator* */
FA * operator-> () const { return &(facadeLink_(*this)); }
FA * operator-> () const { return &(facadeLink_(*this)); }
/** directly access the instance via the CL interface */
I& get () const { ENSURE(instance_); return *instance_; }

View 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

View file

@ -28,73 +28,87 @@
using util::cStr;
namespace lumiera {
namespace facade {
LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible");
template<class IHA>
class Holder;
template<class FA, class I>
class Holder<InstanceHandle<I,FA> >
: Accessor<FA>,
protected FA
{
protected:
typedef InstanceHandle<I,FA> IHandle;
typedef Holder<IHandle> THolder;
typedef Proxy<IHandle> TProxy;
typedef Accessor<FA> Access;
I& _i_;
Holder (IHandle const& iha)
: _i_(iha.get())
{ }
public:
static TProxy& open(IHandle const& iha)
{
static char buff[sizeof(TProxy)];
TProxy* p = new(buff) TProxy(iha);
Access::implProxy_ = p;
return *p;
}
static void close()
{
if (!Access::implProxy_) return;
TProxy* p = static_cast<TProxy*> (Access::implProxy_);
Access::implProxy_ = 0;
p->~TProxy();
}
};
template<class FA>
FA* Accessor<FA>::implProxy_;
template<class IHA>
void
openProxy (IHA const& iha)
{
Proxy<IHA>::open(iha);
}
template<class IHA>
void
closeProxy ()
{
Proxy<IHA>::close();
}
} // namespace facade
namespace lumiera{
namespace facade {
} // namespace lumiera
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>
class Holder;
template<class FA, class I>
class Holder<InstanceHandle<I,FA> >
: Accessor<FA>
, protected FA
{
protected:
typedef InstanceHandle<I,FA> IHandle;
typedef Holder<IHandle> THolder;
typedef Proxy<IHandle> TProxy;
typedef Accessor<FA> Access;
I& _i_;
Holder (IHandle const& iha)
: _i_(iha.get())
{ }
public:
static TProxy& open(IHandle const& iha)
{
static char buff[sizeof(TProxy)];
TProxy* p = new(buff) TProxy(iha);
Access::implProxy_ = p;
return *p;
}
static void close()
{
if (!Access::implProxy_) return;
TProxy* p = static_cast<TProxy*> (Access::implProxy_);
Access::implProxy_ = 0;
p->~TProxy();
}
};
template<class FA>
FA* Accessor<FA>::implProxy_;
template<class IHA>
void
openProxy (IHA const& iha)
{
Proxy<IHA>::open(iha);
}
template<class IHA>
void
closeProxy ()
{
Proxy<IHA>::close();
}
}} // namespace lumiera::facade

View file

@ -104,9 +104,12 @@ namespace lumiera {
/** initiate termination of this subsystem.
* may be called repeatedly any time...
* @warning must not block nor throw. */
virtual void triggerShutdown () throw() =0;
* 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. */
virtual void triggerShutdown () throw() =0;
const std::vector<Subsys*>
@ -114,7 +117,7 @@ namespace lumiera {
private:
/** weather this subsystem is actually operational.
/** whether this subsystem is actually operational.
* When returning \c false here, the application may
* terminate at any point without further notice
* Note further, that a subsystem must not be in

View file

@ -173,7 +173,7 @@ namespace lumiera {
if (!and_all (susy->getPrerequisites(), isRunning() ))
{
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

View file

@ -50,6 +50,7 @@
#include "common/instancehandle.hpp"
#include "lib/singleton-ref.hpp"
#include "lib/scoped-ptrvect.hpp"
#include "include/logging.h"
#include <glibmm.h>
#include <sigc++/sigc++.h>

View file

@ -23,10 +23,12 @@
#include "gui/gtk-lumiera.hpp"
#include "gui/panels/timeline-panel.hpp"
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
#include "gui/workspace/workspace-window.hpp"
#include "gui/model/project.hpp"
#include "gui/controller/controller.hpp"
#include "lib/util.hpp"
#include <boost/foreach.hpp>
@ -34,6 +36,7 @@
using namespace Gtk;
using namespace sigc;
using namespace gui::widgets;
using namespace gui::widgets::timeline;
using namespace gui::model;
using boost::shared_ptr; ///////////////////////////////TICKET #796
@ -60,6 +63,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
, iBeamTool(Gtk::StockID("tool_i_beam"))
, zoomIn(Stock::ZOOM_IN)
, zoomOut(Stock::ZOOM_OUT)
, zoomScale()
, updatingToolbar(false)
, currentTool(timeline::Arrow)
{
@ -101,14 +105,15 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
toolbar.append(separator2);
toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in));
toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out));
toolbar.append(zoomScale);
zoomScale.signal_zoom().
connect(mem_fun(this,&TimelinePanel::on_zoom));
toolbar.show_all();
panelBar.pack_start(toolbar, PACK_SHRINK);
// Setup tooltips
sequenceChooser .set_tooltip_text(_("Change sequence"));
sequenceChooser .set_tooltip_text(_("Change sequence"));
previousButton .set_tooltip_text(_("To beginning"));
rewindButton .set_tooltip_text(_("Rewind"));
@ -122,12 +127,18 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
zoomIn .set_tooltip_text(_("Zoom in"));
zoomOut .set_tooltip_text(_("Zoom out"));
zoomScale .set_tooltip_text(_("Adjust timeline zoom scale"));
// 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();
timelineWidget.reset(new TimelineWidget(load_state(sequence)));
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
update_sequence_chooser();
@ -136,7 +147,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
show_time (Time::ZERO);
}
const char*
TimelinePanel::get_title()
{
@ -183,6 +193,13 @@ TimelinePanel::on_ibeam_tool()
set_tool(timeline::IBeam);
}
void
TimelinePanel::on_zoom(double time_scale_ratio)
{
REQUIRE(timelineWidget);
timelineWidget->zoom_view(time_scale_ratio);
}
void
TimelinePanel::on_zoom_in()
{
@ -236,18 +253,19 @@ TimelinePanel::on_sequence_chosen()
{
weak_ptr<Sequence> sequence_ptr =
(*iter)[sequenceChooserColumns.sequenceColumn];
shared_ptr<Sequence> sequence(sequence_ptr.lock());
if(sequence)
{
shared_ptr<timeline::TimelineState> old_state(
timelineWidget->get_state());
REQUIRE(old_state);
if(sequence != old_state->get_sequence())
timelineWidget->set_state(load_state(sequence));
}
}
update_zoom_buttons();
}
@ -379,9 +397,10 @@ TimelinePanel::on_frame()
shared_ptr<timeline::TimelineState>
TimelinePanel::load_state(weak_ptr<Sequence> sequence)
{
/* state exists */
if(contains(timelineStates, sequence))
return timelineStates[sequence];
return timelineStates[sequence];
shared_ptr<Sequence> shared_sequence = sequence.lock();
if(shared_sequence)
{

View file

@ -32,6 +32,8 @@
#include "gui/panels/panel.hpp"
#include "gui/widgets/timecode-widget.hpp"
#include "gui/widgets/timeline-widget.hpp"
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
#include "lib/time/timevalue.hpp"
#include <boost/scoped_ptr.hpp>
@ -87,6 +89,7 @@ private:
void on_arrow_tool();
void on_ibeam_tool();
void on_zoom(double time_scale_ratio);
void on_zoom_in();
void on_zoom_out();
@ -171,7 +174,7 @@ private:
boost::scoped_ptr<TimelineWidget> timelineWidget;
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;
// Toolbar Widgets
@ -191,6 +194,7 @@ private:
MiniButton zoomIn;
MiniButton zoomOut;
gui::widgets::timeline::TimelineZoomScale zoomScale;
Gtk::SeparatorToolItem separator2;

View file

@ -41,7 +41,7 @@ const int TimelineWidget::TrackPadding = 1;
const int TimelineWidget::HeaderWidth = 150;
const int TimelineWidget::HeaderIndentWidth = 10;
const double TimelineWidget::ZoomIncrement = 1.25;
const int64_t TimelineWidget::MaxScale = 30000000;
const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million
TimelineWidget::TimelineWidget(
boost::shared_ptr<timeline::TimelineState> source_state) :
@ -102,6 +102,7 @@ TimelineWidget::get_state()
void
TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
{
state = new_state;
// Clear the track tree
@ -125,17 +126,17 @@ TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
update_tracks();
// Send the state changed signal
stateChangedSignal.emit();
stateChangedSignal.emit (state);
}
void
TimelineWidget::zoom_view(int zoom_size)
TimelineWidget::zoom_view(double timescale_ratio)
{
if(state)
{
const int view_width = body->get_allocation().get_width();
state->get_view_window().zoom_view(view_width / 2, zoom_size);
}
{
const int view_width = body->get_allocation().get_width();
state->get_view_window().zoom_view(view_width / 2, timescale_ratio);
}
}
ToolType
@ -178,7 +179,7 @@ TimelineWidget::hovering_track_changed_signal() const
return hoveringTrackChangedSignal;
}
sigc::signal<void>
TimelineWidget::TimelineStateChangeSignal
TimelineWidget::state_changed_signal() const
{
return stateChangedSignal;
@ -212,10 +213,12 @@ TimelineWidget::on_view_window_changed()
if(state)
{
timeline::TimelineViewWindow &window = state->get_view_window();
const int view_width = body->get_allocation().get_width();
horizontalAdjustment.set_page_size(
window.get_time_scale() * view_width);
horizontalAdjustment.set_value(_raw(window.get_time_offset()));
}
}

View file

@ -98,7 +98,7 @@ public:
* @param zoom_size The number of steps to zoom by. The scale factor
* 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.
@ -115,14 +115,16 @@ public:
public:
/* ===== 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> playback_period_drag_released_signal() const;
sigc::signal<void, boost::shared_ptr<timeline::Track> >
hovering_track_changed_signal() const;
HoveringTrackChangedSignal hovering_track_changed_signal() const;
sigc::signal<void> state_changed_signal() const;
TimelineStateChangeSignal state_changed_signal() const;
/* ===== Events ===== */
protected:
@ -275,9 +277,8 @@ protected:
// Signals
sigc::signal<void, Time> mouseHoverSignal;
sigc::signal<void> playbackPeriodDragReleasedSignal;
sigc::signal<void, boost::shared_ptr<timeline::Track> >
hoveringTrackChangedSignal;
sigc::signal<void> stateChangedSignal;
HoveringTrackChangedSignal hoveringTrackChangedSignal;
TimelineStateChangeSignal stateChangedSignal;
bool update_tracks_frozen;

View file

@ -39,6 +39,8 @@ using namespace lumiera;
using gui::util::CairoUtil;
using boost::shared_ptr; ////////////////////TICKET #796
namespace gui {
namespace widgets {
namespace timeline {
@ -61,7 +63,7 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget)
register_styles();
// Reset the state
on_state_changed();
propagateStateChange();
}
TimelineBody::~TimelineBody()
@ -69,6 +71,13 @@ TimelineBody::~TimelineBody()
WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here");
}
TimelineViewWindow&
TimelineBody::viewWindow() const
{
REQUIRE(timelineState);
return timelineState->get_view_window();
}
TimelineWidget&
TimelineBody::getTimelineWidget () const
{
@ -147,7 +156,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event)
// Makes sure the widget styles have been loaded
read_styles();
if(timelineWidget.get_state())
if (timelineState)
{
// Prepare to render via cairo
const Allocation allocation = get_allocation();
@ -169,9 +178,9 @@ TimelineBody::on_scroll_event (GdkEventScroll* event)
{
REQUIRE(event != NULL);
if(timelineWidget.get_state())
if (timelineState)
{
TimelineViewWindow &window = view_window();
TimelineViewWindow &window = viewWindow();
const Allocation allocation = get_allocation();
if(event->state & GDK_CONTROL_MASK)
@ -255,17 +264,16 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
{
REQUIRE(event != NULL);
if(timelineWidget.get_state())
if (timelineState)
{
// Handle a middle-mouse drag if one is occuring
switch(dragType)
{
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!
TimelineViewWindow &window = viewWindow();
const int64_t scale = window.get_time_scale();
window.set_time_offset(beginShiftTimeOffset
+ TimeValue(scale * (mouseDownX - event->x)));
@ -294,12 +302,20 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
}
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
view_window().changed_signal().connect(
viewWindow().changed_signal().connect(
sigc::mem_fun(this, &TimelineBody::on_update_view) );
}
@ -373,7 +389,7 @@ TimelineBody::draw_track(Cairo::RefPtr<Cairo::Context> cr,
// Render the track
cr->save();
TimelineViewWindow &window = view_window();
TimelineViewWindow &window = viewWindow();
timeline_track->draw_track(cr, &window);
cr->restore();
}
@ -386,12 +402,11 @@ TimelineBody::draw_selection(Cairo::RefPtr<Cairo::Context> cr)
// Prepare
const Allocation allocation = get_allocation();
shared_ptr<TimelineState> state = timelineWidget.get_state();
REQUIRE(state);
REQUIRE(timelineState);
TimelineViewWindow const& window = state->get_view_window();
const int start_x = window.time_to_x(state->getSelectionStart());
const int end_x = window.time_to_x(state->getSelectionEnd());
TimelineViewWindow const& window = timelineState->get_view_window();
const int start_x = window.time_to_x(timelineState->getSelectionStart());
const int end_x = window.time_to_x(timelineState->getSelectionEnd());
// Draw the cover
if(end_x > 0 && start_x < allocation.get_width())
@ -427,16 +442,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr)
{
REQUIRE(cr);
// Prepare
shared_ptr<TimelineState> state = timelineWidget.get_state();
if(state)
if (timelineState)
{
if (!state->isPlaying()) return;
if (!timelineState->isPlaying()) return;
const Allocation allocation = get_allocation();
Time point = state->getPlaybackPoint();
const int x = view_window().time_to_x(point);
Time point = timelineState->getPlaybackPoint();
const int x = viewWindow().time_to_x(point);
// Set source
cr->set_source(playbackPointColour);
@ -455,10 +467,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr)
void
TimelineBody::begin_shift_drag()
{
if(timelineWidget.get_state())
if (timelineState)
{
dragType = Shift;
beginShiftTimeOffset = view_window().get_time_offset();
beginShiftTimeOffset = viewWindow().get_time_offset();
beginShiftVerticalOffset = get_vertical_offset();
}
}
@ -475,14 +487,6 @@ TimelineBody::set_vertical_offset(int 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
TimelineBody::register_styles() const
{

View file

@ -68,10 +68,7 @@ public:
*/
TimelineBody(gui::widgets::TimelineWidget &timeline_widget);
/**
* Destructor
*/
~TimelineBody();
virtual ~TimelineBody();
TimelineWidget&
getTimelineWidget () const;
@ -125,13 +122,18 @@ protected:
bool on_motion_notify_event(GdkEventMotion *event);
/**
* The event handler for when the TimelineWidget's state object is
* replaced.
* The event handler for when the TimelineWidget's state is switched.
*/
void on_state_changed();
void on_state_changed (boost::shared_ptr<TimelineState> newState);
/* ===== Internals ===== */
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.
@ -161,13 +163,9 @@ private:
void set_vertical_offset(int offset);
/**
* A helper function to get the view window
* @remarks This function must not be called unless the TimlineWidget
* has a valid state.
*/
TimelineViewWindow& view_window() const;
/** adjust to the new timeline state */
void propagateStateChange();
/**
* Registers all the styles that this class will respond to.
*/
@ -202,6 +200,8 @@ private:
Cairo::RefPtr<Cairo::SolidPattern> playbackPointColour;
gui::widgets::TimelineWidget &timelineWidget;
boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr
friend class Tool;
friend class ArrowTool;

View file

@ -75,6 +75,14 @@ TimelineRuler::TimelineRuler (TimelineWidget &timeline_widget)
register_styles();
}
TimelineViewWindow&
TimelineRuler::viewWindow() const
{
REQUIRE(timelineState);
return timelineState->get_view_window();
}
void
TimelineRuler::set_mouse_chevron_offset(int offset)
{
@ -114,7 +122,7 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
if(!window)
return false;
if(timelineWidget.get_state())
if (timelineState)
{
// Prepare to render via cairo
const Allocation allocation = get_allocation();
@ -160,11 +168,11 @@ TimelineRuler::on_button_press_event(GdkEventButton* event)
{
REQUIRE(event != NULL);
if(timelineWidget.get_state())
if (timelineState)
{
if(event->button == 1)
{
pinnedDragTime = view_window().x_to_time(event->x);
pinnedDragTime = viewWindow().x_to_time(event->x);
isDragging = true;
}
}
@ -219,34 +227,40 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation)
}
void
TimelineRuler::on_state_changed()
TimelineRuler::on_state_changed (shared_ptr<TimelineState> newState)
{
if(timelineWidget.get_state())
{
// Connect up some events
view_window().changed_signal().connect(
sigc::mem_fun(this, &TimelineRuler::on_update_view) );
}
REQUIRE (newState);
timelineState = newState;
propagateStateChange();
}
void
TimelineRuler::propagateStateChange()
{
// Connect up some events
viewWindow().changed_signal().connect(
sigc::mem_fun(this, &TimelineRuler::on_update_view) );
// Redraw
on_update_view();
}
void
TimelineRuler::set_leading_x(const int x)
{
shared_ptr<TimelineState> state = timelineWidget.get_state();
if(state)
if (timelineState)
{
TimeVar newStartPoint (view_window().x_to_time(x));
TimeVar newStartPoint (viewWindow().x_to_time(x));
Offset selectionLength (pinnedDragTime, newStartPoint);
if (newStartPoint > pinnedDragTime)
newStartPoint=pinnedDragTime; // use the smaller one as selection start
state->setPlaybackPeriod (Mutation::changeTime(newStartPoint) );
state->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
timelineState->setPlaybackPeriod (Mutation::changeTime(newStartPoint) );
timelineState->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once
////////////////////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_height() > 0);
const TimelineViewWindow &window = view_window();
const TimelineViewWindow &window = viewWindow();
const gavl_time_t left_offset = _raw(window.get_time_offset());
const int64_t time_scale = window.get_time_scale();
@ -361,16 +375,15 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr,
REQUIRE(cr);
REQUIRE(ruler_rect.get_width() > 0);
REQUIRE(ruler_rect.get_height() > 0);
REQUIRE(timelineState);
shared_ptr<TimelineState> state = timelineWidget.get_state();
REQUIRE(state);
const TimelineViewWindow &window = state->get_view_window();
const TimelineViewWindow &window = timelineState->get_view_window();
Glib::RefPtr<Style> style = get_style();
Gdk::Cairo::set_source_color(cr, style->get_fg(STATE_NORMAL));
// 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())
{
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
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())
{
cr->move_to(b, ruler_rect.get_height());
@ -397,18 +410,17 @@ TimelineRuler::draw_playback_period(Cairo::RefPtr<Cairo::Context> cr,
REQUIRE(cr);
REQUIRE(ruler_rect.get_width() > 0);
REQUIRE(ruler_rect.get_height() > 0);
REQUIRE(timelineState);
shared_ptr<TimelineState> state = timelineWidget.get_state();
REQUIRE(state);
const TimelineViewWindow &window = state->get_view_window();
const TimelineViewWindow &window = timelineState->get_view_window();
// Calculate coordinates
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 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;
@ -465,14 +477,12 @@ TimelineRuler::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr,
REQUIRE(cr);
REQUIRE(ruler_rect.get_width() > 0);
REQUIRE(ruler_rect.get_height() > 0);
REQUIRE(timelineState);
shared_ptr<TimelineState> state = timelineWidget.get_state();
REQUIRE(state);
if (!state->isPlaying()) return;
if (!timelineState->isPlaying()) return;
TimelineViewWindow const& window = state->get_view_window();
Time point = state->getPlaybackPoint();
const int x = window.time_to_x(point);
Time point = timelineState->getPlaybackPoint();
const int x = viewWindow().time_to_x(point);
cr->move_to(x + 0.5, ruler_rect.get_height());
cr->rel_line_to(0, -playbackPointSize);
@ -494,7 +504,6 @@ TimelineRuler::calculate_major_spacing() const
{
unsigned int i;
const int64_t time_scale = view_window().get_time_scale();
const gavl_time_t major_spacings[] = {
GAVL_TIME_SCALE / 1000,
GAVL_TIME_SCALE / 400,
@ -520,6 +529,8 @@ TimelineRuler::calculate_major_spacing() const
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++)
{
const int64_t division_width = major_spacings[i] / time_scale;
@ -531,14 +542,6 @@ TimelineRuler::calculate_major_spacing() const
return major_spacings[i];
}
TimelineViewWindow&
TimelineRuler::view_window() const
{
shared_ptr<TimelineState> state = timelineWidget.get_state();
REQUIRE(state);
return state->get_view_window();
}
void
TimelineRuler::register_styles() const
{

View file

@ -29,6 +29,7 @@
#include "gui/gtk-lumiera.hpp"
#include "lib/time/timevalue.hpp"
#include "gui/widgets/timeline/timeline-state.hpp"
namespace gui {
namespace widgets {
@ -108,10 +109,9 @@ private:
void on_size_allocate(Gtk::Allocation& allocation);
/**
* The event handler for when the TimelineWidget's state object is
* replaced.
* The event handler for when the TimelineWidget's state is switched.
*/
void on_state_changed();
void on_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr
private:
/* ===== Internal Methods ===== */
@ -161,7 +161,13 @@ private:
*/
void draw_playback_point(Cairo::RefPtr<Cairo::Context> cr,
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
* between major graduations on the ruler scale.
@ -170,9 +176,9 @@ private:
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.
@ -233,6 +239,11 @@ private:
*/
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
* be drawn.

View file

@ -51,12 +51,13 @@ TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence
REQUIRE(sequence);
////////////////////////////////////////////////////////////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::changeDuration(Duration(FSecs(2))));
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all
}
boost::shared_ptr<model::Sequence>

View file

@ -103,7 +103,7 @@ public:
* the GUI is really connected to the Player
*/
void setPlaybackPoint(Time newPos);
/**
* A signal to notify when the selected period has changed.
*/
@ -143,10 +143,9 @@ private:
* when actually integrated with the Player
*/
TimeVar playbackPoint_;
bool isPlayback_;
// Signals
/**

View file

@ -31,29 +31,29 @@ namespace gui {
namespace widgets {
namespace timeline {
TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale)
: timeOffset(offset)
, timeScale(scale)
{
TODO("Create a function to limit timescale between 1 and MaxScale");
TODO("TICKET #795 Some functions need to be private");
}
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);
}
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;
changedSignal.emit();
}
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;
}
@ -66,21 +66,39 @@ TimelineViewWindow::set_time_scale(int64_t scale)
}
void
TimelineViewWindow::zoom_view(int point, int zoom_size)
{
int64_t new_time_scale = (double)timeScale * pow(1.25, -zoom_size);
// Limit zooming in too close
if(new_time_scale < 1) new_time_scale = 1;
// Nudge zoom problems caused by integer rounding
if(new_time_scale == timeScale && zoom_size < 0)
new_time_scale++;
// Limit zooming out too far
TimelineViewWindow::set_time_scale(double ratio)
{
int64_t max = TimelineWidget::MaxScale;
int64_t min = 1;
if(ratio <= 0.0)
{
set_time_scale((int64_t)min);
return;
}
if(ratio > 1.0)
{
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;
if(new_time_scale > TimelineWidget::MaxScale)
new_time_scale = TimelineWidget::MaxScale;
// The view must be shifted so that the zoom is centred on the cursor
TimeVar newStartPoint = get_time_offset();
newStartPoint += TimeValue(point * (timeScale - new_time_scale));

View file

@ -94,14 +94,14 @@ public:
* zero
*/
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
* given point on the timeline still.
* @param zoom_size The number of steps to zoom by. The scale factor
* is 1.25^(-zoom_size).
* @param new_time_scale The number of steps to zoom by. The scale factor
* 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.

View 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

View 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 */

View file

@ -21,13 +21,18 @@
*/
/** @file display-facade.h
** Major public Interface of the Lumiera GUI. While, generally speaking, the GUI
** controls the application and thus acts on its own, it exposes some services
** to the lower layers. Especially the lumiera::Display interface serves to
** hand over calculated frames to the GUI for displaying them in a viewer.
** It's a first draft as of 1/2009, probably it can be factored out into
** a more general display service in future.
**
** Experimental Interface, allowing the Dummy-Player to access the
** video display widget in the GUI. While, generally speaking, the GUI
** controls the application and thus acts on its own, it might expose some
** services to the lower layers.
**
** 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 dummy-player-facade.h
**

View file

@ -35,7 +35,6 @@
#include "include/interfaceproxy.hpp"
#include "lib/handle.hpp"
#include <boost/noncopyable.hpp>

View file

@ -33,7 +33,7 @@
** 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
** 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
** 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.
@ -48,17 +48,18 @@
** 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
** 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
** an InstanceHandle object. When creating such an InstanceHandle using the appropriate
** 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
** the right proxy object instance. Similarly, when the InstanceHandle object goes out
** of scope, prior to detaching from the Interface/Proxy system, the corresponding
** lumiera::facade::Accessor factory is "closed", which additionally means destroying
** the proxy object instance and switching any further access to throwing and exception.
**
** system, the corresponding facade::Proxy factory is addressed and the interface instance
** is "opened" by creating the appropriate proxy object instance. Similarly, when the
** InstanceHandle object goes out of scope, prior to detaching from the Interface/Proxy
** system, the corresponding lumiera::facade::Accessor frontend is "closed", which
** 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
** in turn), there needs to be an actual implementation of each proxy object located in
** some translation unit. The usual place is interfaceproxy.cpp, which gets linked into
@ -82,45 +83,66 @@
namespace lumiera {
namespace facade {
/** error-ID for accessing a (currently) closed facade */
LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE);
namespace facade {
/** error-ID for accessing a (currently) closed facade */
LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE);
/*********************************************************************
* 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>
class Accessor
{
protected:
static FA* implProxy_;
public:
FA&
operator() ()
{
if (implProxy_)
return *implProxy_;
else
throw error::State("Facade interface currently closed."
, LUMIERA_ERROR_FACADE_LIFECYCLE);
}
};
/*********************************************************************
*
*/
template<class FA>
class Accessor
{
protected:
static FA* implProxy_;
public:
FA&
operator() ()
{
if (implProxy_)
return *implProxy_;
else
throw error::State("Facade interface currently closed.");
}
};
template<class IHA>
void openProxy (IHA const&);
template<class IHA>
void closeProxy ();
template<class IHA>
class Proxy;
} // namespace facade
template<class IHA>
void openProxy (IHA const&);
} // namespace lumiera
template<class IHA>
void closeProxy ();
template<class IHA>
class Proxy;
}} // namespace lumiera::facade
#endif

163
src/include/play-facade.h Normal file
View 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

View file

@ -34,6 +34,7 @@
** e.g. throwing an exception instead of creating a NULL value.
**
** @see lumiera::WrapperPtr usage example to access a variant record
** @see lib::InPlaceAnyHolder usage example to access a subclass in embedded storage
**
*/

View file

@ -122,6 +122,7 @@ namespace lumiera {
LUMIERA_ERROR_DECLARE (ASSERTION); ///< assertion failure
/* generic error situations */
LUMIERA_ERROR_DECLARE (LIFECYCLE); ///< Lifecycle assumptions violated
LUMIERA_ERROR_DECLARE (WRONG_TYPE); ///< runtime type mismatch
LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached
LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value

View file

@ -79,6 +79,7 @@ namespace lumiera {
LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure");
/* some further generic error situations */
LUMIERA_ERROR_DEFINE (LIFECYCLE, "Lifecycle assumptions violated");
LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch");
LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached");
LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value");

View file

@ -38,11 +38,15 @@
#define FUNCTOR_UTIL_H_
#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::bind;
@ -86,7 +90,7 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
/** convenience shortcut to call two functors in sequence.
* @return a Dispatch functor object which incorporates the
* functors as copy and on invocation calls the first
* functors as copy and on invocation calls the first
* function and then returns the result of the second */
template<typename SIG>
Dispatch<SIG>
@ -99,9 +103,12 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
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.
* Thus we can implement a raw comparison function,
* 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 ==
f2._M_functor._M_unused._M_const_object );
} // 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 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_*/

View file

@ -60,7 +60,7 @@ namespace lib {
* Generic opaque reference counting handle, for accessing a service
* and managing its lifecycle. Usually such a handle is created 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?
* while client code is free to copy and store handle objects.
* Finally, any handle can be closed, thereby decrementing

View file

@ -279,6 +279,14 @@ namespace lib {
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>
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
* all the keys of the given Map or Hashtable
*/
@ -424,6 +454,7 @@ namespace lib {
}
using iter_source::wrapIter;
using iter_source::transform;
using iter_source::eachMapKey;
using iter_source::eachDistinctKey;
using iter_source::eachValForKey;

View file

@ -76,11 +76,11 @@ namespace lumiera {
extern "C" { /* ==== implementation C interface for lifecycle hooks ======= */
extern const char * lumiera_ON_BASIC_INIT = lumiera::ON_BASIC_INIT;
extern 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_BASIC_INIT = lumiera::ON_BASIC_INIT;
const char * lumiera_ON_GLOBAL_INIT = lumiera::ON_GLOBAL_INIT;
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;

View file

@ -22,6 +22,7 @@
/** @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
** arbitrary signatures, often there is the necessity to hold onto such a functor
** while hiding the actual signature behind an common interface ("type erasure").

View 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

View 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

View file

@ -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
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -21,11 +21,32 @@
*/
/** @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
#define LUMIERA_META_TYPELIST_UTIL_H
#include "lib/meta/typelist.hpp"
namespace lumiera {
@ -34,6 +55,7 @@ namespace typelist{
/**
* Metafunction counting the number of Types in the collection
* @return an embedded constant \c value holding the result
*/
template<class TYPES>
struct count;
@ -48,6 +70,7 @@ namespace typelist{
enum{ value = 1 + count<TYPES>::value };
};
/**
* 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
* 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..
* Metafunction to check if a specific type is contained
* in a given typelist. Only exact match is detected.
*/
template<class TYPES>
struct Dissect;
template<class T, class TYPES>
struct Dissect<Node<T,TYPES> >
template<typename TY, typename TYPES>
struct IsInList
{
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 extcluding the last element
typedef typename SplitLast<List>::Type End; ///< the last element
typedef Node<End,NullType> Last; ///< a list containing the last element
enum{ value = false };
};
template<>
struct Dissect<NullType>
template<typename TY, typename TYPES>
struct IsInList<TY, Node<TY,TYPES> >
{
typedef NullType List;
typedef NullType Head;
typedef NullType First;
typedef NullType Tail;
typedef NullType Prefix;
typedef NullType End;
typedef NullType Last;
enum{ value = true };
};
/**
* 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;
};
/** generate all possible on-off combinations of the given flags */
template<class FLAGS>
struct CombineFlags
{
typedef typename Combine<FLAGS, FlagOnOff>::List List;
template<typename TY, typename XX, typename TYPES>
struct IsInList<TY, Node<XX,TYPES> >
{
enum{ value = IsInList<TY,TYPES>::value };
};
/** convenience shortcut: query function */
template<typename TY, typename TYPES>
bool
isInList()
{
return IsInList<TY,TYPES>::value;
}

View file

@ -40,15 +40,25 @@ This code is heavily inspired by
/** @file typelist.hpp
** 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
** 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.
**
** Interface for using this facility is the template Types(.....) for up to 20 Type parameters
** 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.
** 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 typelisttest.cpp
** @see control::CommandSignature more elaborate usage example (dissecting a functor signature)
** @see TypeList_test
** @see TypeListManip_test
**
*/

View file

@ -45,7 +45,7 @@
#define LUMIERA_META_TYPESEQ_UTIL_H
#include "lib/meta/typelist.hpp"
#include "lib/meta/typelist-util.hpp"
#include "lib/meta/typelist-manip.hpp"
#include "lib/meta/util.hpp"

View file

@ -38,6 +38,24 @@ namespace lumiera {
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.
* 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

View file

@ -219,7 +219,7 @@ namespace lib {
char content_[siz];
void* ptr() { return &content_; }
virtual ~Buffer() {}
virtual ~Buffer() {} ///< this is an ABC with VTable
virtual bool isValid() const =0;
virtual bool empty() const =0;
virtual BaseP getBase() const =0;

View file

@ -79,51 +79,51 @@ namespace lumiera {
P ( ) : BASE() {}
template<class Y> explicit P (Y* p) : BASE(p) {}
template<class Y, class D> P (Y* p, D d) : BASE(p,d){}
P (P const& r) : BASE(r) {}
template<class Y> P (shared_ptr<Y> const& r) : BASE(r) {}
template<class Y> explicit P (weak_ptr<Y> const& wr) : BASE(wr) {}
template<class Y> explicit P (std::auto_ptr<Y> & ar) : BASE(ar) {}
template<class Y> explicit P (std::auto_ptr<Y> & ar) : BASE(ar) {}
P& operator= (P const& r) { BASE::operator= (r); return *this; }
P& operator= (P const& r) { BASE::operator= (r); return *this; }
template<class Y> P& operator=(shared_ptr<Y> const& sr) { BASE::operator= (sr); return *this; }
template<class Y> P& operator=(std::auto_ptr<Y> & ar) { BASE::operator= (ar); return *this; }
TAR* get() const { return dynamic_cast<TAR*> (BASE::get()); }
TAR& operator*() const { return *get(); }
TAR* operator->() const { return get(); }
void swap(P& b) { BASE::swap (b);}
private: /* === friend operators injected into enclosing namespace for ADL === */
template<typename _O_>
friend inline bool
operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); }
template<typename _O_>
friend inline bool
operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); }
template<typename _O_>
friend inline bool
operator< (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p < *q; } ///< @note deliberately not allowing comparison on NIL ////TICKET #307 : problem with equality test in associative containers, where equal(a,b) := !(a < b) && !(b < a)
template<typename _O_>
friend inline bool
operator> (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *q < *p; }
template<typename _O_>
friend inline bool
operator<= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p <= *q;}
template<typename _O_>
friend inline bool
operator>= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p >= *q;}
};
} // namespace lumiera
#endif

View file

@ -389,21 +389,21 @@ namespace lib {
}
template<class IMP, typename A1>
PolymorphicValue (IMP*, A1& a1)
PolymorphicValue (IMP*, A1 a1)
{
REQUIRE (siz >= sizeof(IMP));
new(&buf_) IMP (a1);
}
template<class IMP, typename A1, typename A2>
PolymorphicValue (IMP*, A1& a1, A2& a2)
PolymorphicValue (IMP*, A1 a1, A2 a2)
{
REQUIRE (siz >= sizeof(IMP));
new(&buf_) IMP (a1,a2);
}
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));
new(&buf_) IMP (a1,a2,a3);
@ -444,13 +444,13 @@ namespace lib {
Adapter() : IMP() { }
template<typename A1>
Adapter (A1& a1) : IMP(a1) { }
Adapter (A1 a1) : IMP(a1) { }
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>
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 */
};
@ -505,31 +505,31 @@ namespace lib {
static PolymorphicValue
build ()
{
Adapter<IMP>* type_to_build_in_buffer;
Adapter<IMP>* type_to_build_in_buffer(0);
return PolymorphicValue (type_to_build_in_buffer);
}
template<class IMP, typename A1>
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);
}
template<class IMP, typename A1, typename A2>
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);
}
template<class IMP, typename A1, typename A2, typename A3>
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);
}

View file

@ -24,9 +24,9 @@
/** @file result.hpp
** Intermediary value object to represent the result of an operation.
** This operation might have produced a value result or failed with an exception.
** Typically, the Result token used \em inline -- immediately either invoking one
** of the member function or employing the built-in result type conversion. It
** will be copyable iff the result value is copyable. There is an implicit
** Typically, the Result token is used \em inline -- immediately either invoking
** one of the member function or employing the built-in result type conversion.
** 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
** 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.
* It can be created for passing a result produced
* by the operation, or the failure to do so. The value
* can be retrieved by implicit or explicit conversion.
* @throws on any attempt to access the value in case of failure
* Optional Result value or status of some operation.
* It can be created for passing a result produced by the operation, or the
* failure to do so. The value can be retrieved by implicit or explicit conversion.
* @throw error::State on any attempt to access the value in case of failure
* @warning this class has a lot of implicit conversions;
* care should be taken when defining functions
* to take Result instances as parameter....

View file

@ -105,8 +105,8 @@ namespace lib {
pointer address(reference r) const { return par_.address(r); }
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); }
void deallocate(pointer p, size_type n) { return par_.deallocate(p,n); }
void destroy(pointer p) { return par_.destroy(p); }
void deallocate(pointer p, size_type n) { par_.deallocate(p,n); }
void destroy(pointer p) { par_.destroy(p); }
void

View file

@ -173,11 +173,21 @@ namespace lib {
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
clear ()
{
if (created_)
get()->~TY();
created_ = false;
}

View 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

View file

@ -110,7 +110,12 @@ namespace lumiera {
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 { };

View file

@ -50,6 +50,9 @@ using boost::hash_combine;
namespace lib {
const size_t STRING_MAX_RELEVANT = 1000;
/** equality on Symbol values is defined
* based on the content, not the address. */
bool

View file

@ -112,7 +112,7 @@ namespace lib {
/** safety guard: maximum number of chars to process.
* For comparisons, hash calculations etc., when dealing
* 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 ===== */

View file

@ -37,6 +37,8 @@
#include <memory>
#include <vector>
#include <map>
#include <cstdlib>
#include <ctime>
namespace test {
@ -141,6 +143,9 @@ namespace test {
REQUIRE( !isnil(groupID) );
TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
// Seed random number generator
std::srand (std::time (NULL));
if (!testcases.getGroup(groupID))
throw lumiera::error::Invalid ("empty testsuite");
}

View 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

View 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
View 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

View file

@ -41,6 +41,9 @@ namespace time {
const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) );
const Time Time::ZERO;
const Offset Offset::ZERO (Time::ZERO);
/** convenience constructor to build an
* internal Lumiera Time value from the usual parts
@ -116,14 +119,21 @@ namespace time {
}
/** duration of the given number of frames */
Duration::Duration (ulong count, FrameRate const& fps)
: Offset(TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL)))
/** offset by the given number of frames. */
Offset::Offset (int64_t count, FrameRate const& fps)
: 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" */
const Duration Duration::NIL = Offset(TimeValue(0));
const Duration Duration::NIL (Time::ZERO);

View file

@ -48,17 +48,6 @@ namespace time {
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
change (Duration& target) const
{
imposeChange (target, TimeVar(target)+=adjustment_);
imposeChange (target, adjustment_);
}
virtual void
change (TimeSpan& target) const
{
imposeChange (target, TimeVar(target)+=adjustment_);
imposeChange (target, adjustment_);
}
@ -173,7 +162,7 @@ namespace time {
virtual void
change (QuTime& target) const
{
imposeChange (target, TimeVar(target)+=adjustment_);
imposeChange (target, adjustment_);
}
@ -205,7 +194,7 @@ namespace time {
/**
* concrete time value mutation:
* nudge target value by the given number of 'steps',
* relative to either the given grid.
* relative to the given grid.
*/
class NudgeMutation
: public ImposeOffsetMutation
@ -234,6 +223,8 @@ namespace time {
* @note currently the natural grid is hard wired,
* just interpreting the step parameter as
* offset in seconds.
* @see mutation#imposeChange (TimeValue, int)
* @see mutation#imposeChange (QuTime, int)
*/
class NaturalNudgeMutation
: public ClonableMutation
@ -243,31 +234,22 @@ namespace time {
virtual void
change (Duration& target) const
{
imposeChange (target, TimeVar(target)+=Time(FSecs(steps_)));
imposeChange (target, steps_);
}
virtual void
change (TimeSpan& target) const
{
imposeChange (target, TimeVar(target)+=Time(FSecs(steps_)));
imposeChange (target, steps_);
}
/** 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. */
/** @note special treatment: use the quantised time's own grid */
virtual void
change (QuTime& target) const
{
PQuant const& grid (target);
int64_t originalGridPoint = grid->gridPoint(target);
int64_t adjustedGridPoint = originalGridPoint + steps_;
imposeChange (target, grid->timeOf (adjustedGridPoint));
imposeChange (target, steps_);
}

View file

@ -112,7 +112,10 @@ namespace time {
static EncapsulatedMutation nudge (int adjustment, Symbol gridID);
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
/* === 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
#endif

View file

@ -51,10 +51,19 @@ namespace time {
, LUMIERA_ERROR_UNKNOWN_GRID);
return grid_found;
}
}//(End) implementation helpers
PQuant
getDefaultGridFallback()
{
static PQuant globalDefaultGrid (new FixedFrameQuantiser(1));
return globalDefaultGrid; ///////////////////////TICKET #810
};
Grid::~Grid() { } // hint to emit the VTable here...
@ -191,7 +200,7 @@ namespace time {
{
Time gt(gridTime);
TimeVar timePoint = gt + origin_;
timePoint += gridOffset * raster_;
timePoint += gridOffset * Offset(raster_);
return timePoint;
}

View file

@ -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.
* Effectively, a quantiser exposes the value Grid API, but

View file

@ -42,6 +42,11 @@ namespace time {
namespace error = lumiera::error;
// forwards...
class FrameRate;
class TimeSpan;
class Mutation;
/**
* basic constant internal time value.
@ -193,6 +198,11 @@ namespace time {
: TimeValue(TimeVar(target) -= origin)
{ }
Offset (int64_t count, FrameRate const& fps);
static const Offset ZERO;
TimeValue
abs() const
{
@ -241,7 +251,6 @@ namespace time {
* @warning do not mix up gavl_time_t and FSecs */
typedef boost::rational<long> FSecs;
class FrameRate;
/**
* Lumiera's internal time value datatype.
@ -304,8 +313,6 @@ namespace time {
class TimeSpan;
class Mutation;
/**
* Duration is the internal Lumiera time metric.
@ -315,33 +322,63 @@ namespace time {
* possibility to send a \em Mutation message.
*/
class Duration
: public Offset
: public TimeValue
{
/// direct assignment prohibited
Duration& operator= (Duration const&);
public:
Duration (Offset const& distance)
: Offset(distance.abs())
: TimeValue(distance.abs())
{ }
explicit
Duration (TimeValue const& timeSpec)
: Offset(Offset(timeSpec).abs())
: TimeValue(Offset(timeSpec).abs())
{ }
explicit
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 (ulong count, FrameRate const& fps);
Duration (int64_t count, FrameRate const& fps);
static const Duration NIL;
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
Duration::Duration (TimeSpan const& interval)
: Offset(interval.duration())
: TimeValue(interval.duration())
{ }
inline

View file

@ -63,8 +63,8 @@
#ifndef CONTROL_TYPED_ALLOCATION_MANAGER_H
#define CONTROL_TYPED_ALLOCATION_MANAGER_H
#ifndef LIB_TYPED_ALLOCATION_MANAGER_H
#define LIB_TYPED_ALLOCATION_MANAGER_H
//#include "pre.hpp"
#include "lib/error.hpp"

View file

@ -37,15 +37,15 @@
using lib::Cmdline;
using lumiera::Subsys;
using lumiera::AppState;
using lumiera::ON_GLOBAL_INIT;
namespace {
Subsys& engine = backend::EngineFacade::getDescriptor();
Subsys& netNode = backend::NetNodeFacade::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& session = proc::Facade::getSessionDescriptor();
Subsys& playOut = proc::Facade::getPlayOutDescriptor();
Subsys& lumigui = gui::GuiFacade::getDescriptor();
}
@ -63,16 +63,20 @@ main (int argc, const char* argv[])
lumiera::Option options (args);
application.init (options);
session.depends (builder);
// session.depends (builder);
netNode.depends (session);
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 (engine);
player.depends (playOut); //////TODO dummy player, until we're able to render
lumigui.depends (player);
script.depends (session);
script.depends (engine);
application.maybeStart (session);
application.maybeStart (playOut);
application.maybeStart (netNode);
application.maybeStart (lumigui);
application.maybeStart (script);

View file

@ -48,6 +48,7 @@
#include "lib/hash-indexed.hpp"
#include "lib/util.hpp"
#include <boost/functional/hash.hpp>
#include <boost/operators.hpp>
#include <iostream>
#include <string>

70
src/proc/asset/viewer.cpp Normal file
View 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
View 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

View file

@ -54,7 +54,6 @@
namespace control {
using lib::TypedAllocationManager;
using lib::InPlaceBuffer;
using std::string;

View file

@ -67,7 +67,7 @@
#include "lib/bool-checkable.hpp"
#include "lib/meta/function.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/util.hpp"

View file

@ -57,7 +57,6 @@
namespace control {
using lib::TypedAllocationManager;
using std::tr1::function;
using std::tr1::shared_ptr;

View file

@ -30,7 +30,7 @@
** we need to accept functor objects with a very specific and predetermined
** 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)
** - captureUndo: MEM(P1,..PN)
** - undoOperation void(P1,..PN,MEM)
@ -47,14 +47,11 @@
#define CONTROL_COMMAND_SIGNATURE_H
//#include "pre.hpp"
//#include "lib/symbol.hpp"
#include "lib/meta/function.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/tuple.hpp"
//#include <tr1/memory>
#include <tr1/functional>
@ -62,24 +59,20 @@
namespace control {
// using lib::Symbol;
// using std::tr1::shared_ptr;
using std::tr1::function;
using lumiera::typelist::FunctionSignature;
using lumiera::typelist::FunctionTypedef;
using lumiera::typelist::Types;
//using lumiera::typelist::NullType;
//using lumiera::typelist::Tuple;
using lumiera::typelist::Append;
using lumiera::typelist::SplitLast;
/**
* 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
* 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.
*/
template<typename SIG, typename MEM>
@ -93,7 +86,7 @@ namespace control {
public:
typedef typename FunctionTypedef<void, Args>::Sig OperateSig;
typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig;
typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig;
typedef typename FunctionTypedef<void, ExtendedArgs>::Sig UndoOp_Sig;
typedef Args CmdArgs;
typedef MEM Memento;
@ -103,16 +96,23 @@ namespace control {
/**
* Type analysis helper template.
* Type analysis helper template.
* Used for dissecting a given type signature to derive
* the related basic operation signature, the signature of a possible Undo-function
* and the signature necessary for capturing undo information. The implementation
* relies on re-binding an embedded type defining template, based on the actual
* 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>
class UndoSignature
{
// preparation: dissect the function signature into arguments and result
typedef typename FunctionSignature< function<SIG> >::Args Args;
typedef typename FunctionSignature< function<SIG> >::Ret Ret;

View file

@ -67,6 +67,7 @@ namespace control {
* It is returned when invoking a HandlingPattern
* and can be used to check for success and/or re-throw
* any Exception encountered during the command execution.
* @todo couldn't that be replaced by a lib::Result<void> instance??
*/
class ExecResult
: public lib::BoolCheckable<ExecResult>

View 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

View 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

View 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

View file

@ -24,12 +24,17 @@
** 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
** 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,
** the node needs to keep a table of buffer pointers, and for releasing the buffers later
** on, we need some handles. The usage pattern of those buffer pointer tables is stack-like,
** thus it makes sense to utilise a single large buffer pointer array per pull() calldown
** sequence and dynamically claim small chunks for each node.
**
** different "buffer providers" (for example the frame cache). Typically, the real buffers
** will be passed as parameters to the actual job instance when scheduled, drawing on the
** results of prerequisite jobs. Yet the actual job implementation remains agnostic with
** respect to the way actual buffers are provided; the invocation just pushes BuffHandle
** 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 nodeoperation.hpp
** @see bufftable.hpp storage for the buffer table
@ -47,67 +52,48 @@
namespace engine {
class BufferProvider;
/**
* 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.
* An opaque descriptor to identify the type and further properties of a data buffer.
* 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
: lib::BoolCheckable<BuffHandle>
class BufferDescriptor
{
typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
typedef Buff* PBuff;
BufferProvider* provider_;
uint64_t subClassification_;
PBuff
operator->() const
{
return pBuffer_;
}
Buff&
operator* () const
{
ENSURE (pBuffer_);
return *pBuffer_;
}
BufferDescriptor(BufferProvider& manager, uint64_t detail)
: provider_(&manager)
, subClassification_(detail)
{ }
bool
isValid() const
{
return pBuffer_;
}
friend class BufferProvider;
public:
// using standard copy operations
//////////////////////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:
PBuff pBuffer_;
long sourceID_;
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;
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
@ -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
#endif

View 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

View file

@ -28,6 +28,7 @@
#include "lib/error.hpp"
#include "proc/engine/buffhandle.hpp"
#include "proc/engine/procnode.hpp"
#include "lib/iter-adapter.hpp"
#include <boost/noncopyable.hpp>
#include <vector>
@ -48,113 +49,108 @@ namespace engine {
* 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;
struct StorageChunk
{ };
template<uint count>
struct Storage
{
enum{size = count * sizeof(StorageChunk)};
};
class Builder
{
public:
Builder& announce (uint count, BufferDescriptor const& type);
BuffTable& build();
};
static Builder& prepare (const size_t STORAGE_SIZE, void* storage);
void lockBuffers();
void releaseBuffers();
typedef vector<BuffHandle> BuffHandleTable;
typedef lib::RangeIter<BuffHandleTable::iterator> iterator;
iterator buffers();
iterator inBuffers();
iterator outBuffers();
};
class BuffTableStorage
{
vector<BuffHandle> 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_];
}
};
/**
* 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
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.");
}
};
/* === Implementation === */
inline BuffTable::Builder&
BuffTable::prepare (const size_t STORAGE_SIZE, void* storage)
{
UNIMPLEMENTED ("expose a builder object for outfitting a buffer pointer table");
}
inline BuffTable::Builder&
BuffTable::Builder::announce (uint count, BufferDescriptor const& type)
{
UNIMPLEMENTED ("accept announcement of additional buffer table entries required");
}
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");
}

View 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

View 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

View 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

View file

@ -1,8 +1,8 @@
/*
BuffHandle - Buffer handling support for the render engine
Dispatcher - translating calculation streams into frame jobs
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
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

View 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

View 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

View 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

View 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

View file

@ -24,7 +24,7 @@
#ifndef 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
{};

View file

@ -56,7 +56,7 @@
#include "proc/state.hpp"
#include "proc/engine/procnode.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
: Invocation
{
@ -179,7 +180,7 @@ namespace engine {
: Invocation(sta, w, outCh) {}
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
@ -189,7 +190,7 @@ namespace engine {
: Invocation(sta, w, outCh) {}
virtual BuffHandle
allocateBuffer (BufferDescriptor const& bd) { return current_.allocateBuffer(bd); }
allocateBuffer (const lumiera::StreamType* ty) { return current_.allocateBuffer(ty); }
};

View file

@ -60,7 +60,7 @@
#include "proc/state.hpp"
#include "proc/engine/procnode.hpp"
#include "proc/engine/buffhandle.hpp"
#include "proc/engine/bufftable.hpp"
#include "proc/engine/bufftable-obsolete.hpp"
#include "proc/engine/nodeinvocation.hpp"
#include "lib/meta/util.hpp"
@ -245,7 +245,7 @@ namespace config {
template<class NEXT>
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
step (Invocation& ivo)
{

View file

@ -26,7 +26,7 @@
#include "proc/engine/nodeoperation.hpp"
#include "proc/engine/nodewiring-config.hpp"
#include "lib/meta/typelist-util.hpp"
#include "lib/meta/typelist-manip.hpp"
namespace engine {

View file

@ -24,7 +24,7 @@
#ifndef 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
* will be loaded at runtime using Lumiera's plugin interface.
*/
class PluginAdapter : public Trafo
class PluginAdapter : public ProcNode
{
/////////////
};

View file

@ -37,7 +37,7 @@
//#include "proc/state.hpp"
#include "proc/engine/procnode.hpp"
#include "proc/engine/buffhandle.hpp"
//#include "proc/engine/bufftable.hpp"
//#include "proc/engine/bufftable-obsolete.hpp"

Some files were not shown because too many files have changed in this diff Show more