From f9452f654cf39a4abbbeecfd81bef51aa347b321 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 12 Sep 2008 03:42:32 +0200 Subject: [PATCH 01/66] start media stream type classification framework --- src/common/p.hpp | 2 +- src/common/streamtype.cpp | 33 ++++++++++++ src/common/streamtype.hpp | 99 ++++++++++++++++++++++++++++++++++++ src/proc/asset/struct.hpp | 2 +- src/proc/engine/procnode.hpp | 2 +- src/proc/stypemanager.cpp | 33 ++++++++++++ src/proc/stypemanager.hpp | 50 ++++++++++++++++++ wiki/renderengine.html | 15 +++--- 8 files changed, 226 insertions(+), 10 deletions(-) create mode 100644 src/common/streamtype.cpp create mode 100644 src/common/streamtype.hpp create mode 100644 src/proc/stypemanager.cpp create mode 100644 src/proc/stypemanager.hpp diff --git a/src/common/p.hpp b/src/common/p.hpp index d4cc2f11c..0a303ace7 100644 --- a/src/common/p.hpp +++ b/src/common/p.hpp @@ -63,7 +63,7 @@ namespace lumiera * std::tr1::shared_ptr, but forwarding type relationships and * ordering operators to the pointee objects. * @param TAR the visible pointee type - * @param the shared-ptr type used as implementation + * @param BASE the shared-ptr type used as implementation * @note if the BASE smart-ptr type used as implementation * implies another pointer type than the one used on * the interface (=type TAR), then every access to the diff --git a/src/common/streamtype.cpp b/src/common/streamtype.cpp new file mode 100644 index 000000000..b54bd5751 --- /dev/null +++ b/src/common/streamtype.cpp @@ -0,0 +1,33 @@ +/* + StreamType - classification of media stream types + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 "common/streamtype.hpp" + + +namespace lumiera + { + + /** */ + + +} // namespace lumiera diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp new file mode 100644 index 000000000..5e3a3592b --- /dev/null +++ b/src/common/streamtype.hpp @@ -0,0 +1,99 @@ +/* + STREAMTYPE.hpp - classification of media stream types + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 LUMIERA_STREAMTYPE_HPP +#define LUMIERA_STREAMTYPE_HPP + + +#include "common/query.hpp" + +#include + + +namespace lumiera + { + + + /** + * + */ + struct StreamType : boost::noncopyable + { + enum MediaKind + { + VIDEO, + IMMAGE, + AUDIO, + MIDI + }; + + enum Usage + { + RAW, + SOURCE, + TARGET, + INTERMEDIARY + }; + + struct Prototype; + + class ImplFacade; + + + MediaKind kind; + Prototype const& prototype; + ImplFacade * implType; + Usage usageTag; + + }; + + + + /** + * + */ + struct StreamType::Prototype + { + Symbol id; + + bool subsumes (Prototype const& other) const; + bool canConvert (Prototype const& other) const; + }; + + + + /** + * + */ + class StreamType::ImplFacade + { + public: + Symbol libraryID; + + }; + + + + +} // namespace lumiera +#endif diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp index 5941bd13a..530f1efa6 100644 --- a/src/proc/asset/struct.hpp +++ b/src/proc/asset/struct.hpp @@ -74,7 +74,7 @@ namespace asset /** * key abstraction: structural asset - * @todo just a stub, have to figure out what a asset::Proc is + * @todo just a stub, have to figure out what a asset::Struct is */ class Struct : public Asset { diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp index 343c6a6e8..9cf3a2ea8 100644 --- a/src/proc/engine/procnode.hpp +++ b/src/proc/engine/procnode.hpp @@ -61,7 +61,7 @@ namespace engine { typedef ProcNode* PNode; template - struct RefArray + struct RefArray ///< @todo need an implementation and then probably move it into library { virtual E const& operator[] (uint i) const =0; virtual ~RefArray() {} diff --git a/src/proc/stypemanager.cpp b/src/proc/stypemanager.cpp new file mode 100644 index 000000000..58eb23944 --- /dev/null +++ b/src/proc/stypemanager.cpp @@ -0,0 +1,33 @@ +/* + STypeManager - entry point for dealing with media stream types + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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/stypemanager.hpp" + +namespace proc_interface + { + + /** */ + + + +} // namespace proc_interface diff --git a/src/proc/stypemanager.hpp b/src/proc/stypemanager.hpp new file mode 100644 index 000000000..d7eeef836 --- /dev/null +++ b/src/proc/stypemanager.hpp @@ -0,0 +1,50 @@ +/* + STYPEMANAGER.hpp - entry point for dealing with media stream types + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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_STATE_H +#define PROC_INTERFACE_STATE_H + + +#include "common/streamtype.hpp" + + + +namespace proc_interface { + + using lumiera::Symbol; + + + class STypeManager + { + protected: + virtual ~STypeManager() {}; + + + public: + lumiera::StreamType const& getType (Symbol sTypeID) ; + }; + + + +} // namespace proc_interface +#endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 64559e1e0..d5d2bda01 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3310,22 +3310,23 @@ if (oldText.indexOf("SplashScreen")==-1) * in a future version, it may also encapsulate the communication in a distributed render farm -
+
The stream Prototype is part of the specification of a media stream's type. It is a semantic (or problem domain oriented) concept and should be distinguished from the actual implementation type of the media stream. The latter is provided by an [[library implementation|StreamTypeImplFacade]]. While there are some common predefined prototypes, mostly, they are defined within the concrete [[Session]] according to the user's needs. 
 
-Prototypes form an open (extensible) collection, though each prototype belongs to a specific media kind ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}). The ''distinguishing property'' of a stream prototype is that any [[Pipe]] can process //streams of a specific prototype only.// Thus, two streams with different prototype can be considered "something quite different" from the users point of view, while two streams belonging to the same prototype can be considered equivalent (and will be converted automatically when their implementation types differ). Note this definition is //deliberately fuzzy,// because it depends on the actual situation of the project in question.
+Prototypes form an open (extensible) collection, though each prototype belongs to a specific media kind ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}).
+The ''distinguishing property'' of a stream prototype is that any [[Pipe]] can process //streams of a specific prototype only.// Thus, two streams with different prototype can be considered "something quite different" from the users point of view, while two streams belonging to the same prototype can be considered equivalent (and will be converted automatically when their implementation types differ). Note this definition is //deliberately fuzzy,// because it depends on the actual situation of the project in question.
 
 Consequently, as we can't get away with an fixed Enum of all stream prototypes, the implementation must rely on a query interface. The intention is to provide a basic set of rules for deciding queries about the most common stream prototypes; besides, a specific session may inject additional rules or utilize a completely different knowledge base. Thus, for a given StreamTypeDescriptor specifying a prototype
 * we can get a [[default|DefaultsManagement]] implementation type
 * we can get a default prototype to a given implementation type by a similar query
-* we can query if a implementation type in question can be //subsumed// under this prototype
+* we can query if a implementation type in question can be //subsumed// by this prototype
 * we can determine if another prototype is //convertible//
 
 !!Examples
-NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus dts,...
+NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus linear PCM...
 
-
+
//how to classify and describe media streams//
 Media data is understood to appear structured as stream(s) over time. While there may be an inherent internal structuring, at a given perspective ''any stream is a unit and homogeneous''. In the context of digital media data processing, streams are always ''quantized'', which means they appear as a temporal sequence of data chunks called ''frames''.
 
@@ -3347,8 +3348,8 @@ A stream type is denoted by a StreamTypeID, which is an identifier, acting as an
 !! Classification
 Within the Proc-Layer, media streams are treated largely in a similar manner. But, looking closer, note everything can be connected together, while on the other hand there may be some classes of media streams which can be considered //equivalent// in most respects. Thus, it seems reasonable to separate the distinction between various media streams into several levels
 * Each media belongs to a fundamental ''kind'' of media, examples being __Video__, __Image__, __Audio__, __MIDI__,... Media streams of different kind can be considered somewhat "completely separate" &mdash; just the handling of each of those media kinds follows a common //generic pattern// augmented with specialisations. Basically, it is //impossible to connect// media streams of different kind. Under some circumstances there may be the possibility of a //transformation// though. For example, a still image can be incorporated into video, sound may be visualized, MIDI may control a sound synthesizer.
-* Below the level of distinct kinds of media streams, within every kind we have an open ended collection of ''prototypes'', which, when compared directly may each be quite distinct and different, but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video lacking depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating sound systems (5.1, 7.1,...), we have common stereophonic and monaural audio. It is considered important to retain some openness and configurability within this level of distinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable for some production to distinguish between digitized film and video NTSC and PAL, while in another production everything is just "video" and can be converted mostly automatically. The most noticeable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype. (&rarr; [[more|StreamPrototype]])
-* Besides the distinction by prototypes, there are the various media ''implementation types''. This classification is not necessarily hierarchically related to the prototype classification, while in practice commonly there will be some sort of dependency. For example, both stereophonic and monaural audio may be implemented as 96kHz 24bit PCM with just a different number of channel streams, as well as we may have a dedicated stereo audio stream with two channels multiplexed into a single stream. For dealing with media streams of various implementation type, we need library routines, which also yield a type classification system. Most notably, for raw sound and video data we use the GAVL library, which defines a classification system for buffers and streams.
+* Below the level of distinct kinds of media streams, within every kind we have an open ended collection of ''prototypes'', which, when compared directly may each be quite distinct and different, but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video lacking depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating sound systems (5.1, 7.1,...), we have common stereophonic and monaural audio. It is considered important to retain some openness and configurability within this level of distinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable for some production to distinguish between digitized film and video NTSC and PAL, while in another production everything is just "video" and can be converted automatically. The most noticeable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype. (&rarr; [[more|StreamPrototype]])
+* Besides the distinction by prototypes, there are the various media ''implementation types''. This classification is not necessarily hierarchically related to the prototype classification, while in practice commonly there will be some sort of dependency. For example, both stereophonic and monaural audio may be implemented as 96kHz 24bit PCM with just a different number of channel streams, as well we may have a dedicated stereo audio stream with two channels multiplexed into a single stream. For dealing with media streams of various implementation type, we need //library// routines, which also yield a //type classification system.// Most notably, for raw sound and video data we use the GAVL library, which defines a classification system for buffers and streams.
 * Besides the type classification detailed thus far, we introduce an ''intention tag''. This is a synthetic classification owned by Lumiera and used for internal wiring decisions. Currently (8/08), we recognize the following intention tags: __Source__, __Raw__, __Intermediary__ and __Target__. Only media streams tagged as __Raw__ can be processed.
 
 !! Media handling requirements involving stream type classification

From d4e3405f09d0bfdb2eacd68e5fe9911d05cb0306 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 13 Sep 2008 03:59:36 +0200
Subject: [PATCH 02/66] some namespace rearrangements

---
 src/common/streamtype.cpp                     |   1 +
 src/common/streamtype.hpp                     |  10 ++
 .../controller => control}/pathmanager.cpp    |  26 ++--
 .../controller => control}/pathmanager.hpp    |  37 +++--
 .../controller => control}/renderstate.hpp    |  45 +++---
 src/proc/{ => control}/stypemanager.cpp       |   8 +-
 src/proc/{ => control}/stypemanager.hpp       |  13 +-
 src/proc/engine/nodewiringconfig.hpp          |   3 +
 src/proc/lumiera.hpp                          |  74 +++++++++-
 uml/lumiera/128261                            |   4 +-
 uml/lumiera/128517                            | 128 +++++++++++++++++-
 uml/lumiera/129029                            |  25 +++-
 uml/lumiera/129285                            |   4 +-
 uml/lumiera/129413                            |  42 +++++-
 uml/lumiera/129669                            |   6 +-
 uml/lumiera/130181                            |   4 +-
 uml/lumiera/130693                            |  50 ++++++-
 uml/lumiera/131077                            |   6 +-
 uml/lumiera/132485.diagram                    |  49 +++++++
 uml/lumiera/5.session                         |  19 +--
 20 files changed, 448 insertions(+), 106 deletions(-)
 rename src/proc/{mobject/controller => control}/pathmanager.cpp (77%)
 rename src/proc/{mobject/controller => control}/pathmanager.hpp (64%)
 rename src/proc/{mobject/controller => control}/renderstate.hpp (55%)
 rename src/proc/{ => control}/stypemanager.cpp (91%)
 rename src/proc/{ => control}/stypemanager.hpp (89%)
 create mode 100644 uml/lumiera/132485.diagram

diff --git a/src/common/streamtype.cpp b/src/common/streamtype.cpp
index b54bd5751..a230ba7d4 100644
--- a/src/common/streamtype.cpp
+++ b/src/common/streamtype.cpp
@@ -28,6 +28,7 @@ namespace lumiera
   {
 
    /** */
+   int bla = 3;
   
 
 } // namespace lumiera
diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp
index 5e3a3592b..86bf9a6bf 100644
--- a/src/common/streamtype.hpp
+++ b/src/common/streamtype.hpp
@@ -20,6 +20,16 @@
  
 */
 
+/** @file streamtype.hpp
+ ** Framework for classification of media streams.
+ ** Besides the actual implementation type of a media stream, the Proc-Layer
+ ** needs a more general way for accessing, comparing and manipulating media streams
+ ** based on type information.
+ ** 
+ ** @see control::STypeManager
+ ** 
+ */
+
 
 #ifndef LUMIERA_STREAMTYPE_HPP
 #define LUMIERA_STREAMTYPE_HPP
diff --git a/src/proc/mobject/controller/pathmanager.cpp b/src/proc/control/pathmanager.cpp
similarity index 77%
rename from src/proc/mobject/controller/pathmanager.cpp
rename to src/proc/control/pathmanager.cpp
index bf3c359ab..cf2a01a25 100644
--- a/src/proc/mobject/controller/pathmanager.cpp
+++ b/src/proc/control/pathmanager.cpp
@@ -21,23 +21,19 @@
 * *****************************************************/
 
 
-#include "proc/mobject/controller/pathmanager.hpp"
+#include "proc/control/pathmanager.hpp"
 
-namespace mobject
+namespace control {
+
+
+
+  engine::Processor *
+  PathManager::buildProcessor ()
   {
-  namespace controller
-    {
+    UNIMPLEMENTED ("build a complete processor and optimize render path");
+    return 0;//////////////////TODO
+  }
 
 
 
-    engine::Processor *
-    PathManager::buildProcessor ()
-    {
-      return 0;//////////////////TODO
-    }
-
-
-
-  } // namespace mobject::controller
-
-} // namespace mobject
+} // namespace control
diff --git a/src/proc/mobject/controller/pathmanager.hpp b/src/proc/control/pathmanager.hpp
similarity index 64%
rename from src/proc/mobject/controller/pathmanager.hpp
rename to src/proc/control/pathmanager.hpp
index a29cd2f94..355c500dd 100644
--- a/src/proc/mobject/controller/pathmanager.hpp
+++ b/src/proc/control/pathmanager.hpp
@@ -21,34 +21,29 @@
 */
 
 
-#ifndef MOBJECT_CONTROLLER_PATHMANAGER_H
-#define MOBJECT_CONTROLLER_PATHMANAGER_H
+#ifndef CONTROL_PATHMANAGER_H
+#define CONTROL_PATHMANAGER_H
 
 #include "proc/engine/processor.hpp"
 
 
 
-namespace mobject
-  {
-  namespace controller
+namespace control {
+  
+  
+  /**
+   * While building a render engine, this Strategy class 
+   * decides on the actual render strategy in accordance
+   * to the current controller settings (system state)
+   */
+  class PathManager
     {
-
-
-    /**
-     * While building a render engine, this Strategy class 
-     * decides on the actual render strategy in accordance
-     * to the current controller settings (system state)
-     */
-    class PathManager
-      {
-      public:
-        engine::Processor* buildProcessor () ;
-        // TODO: allocation, GC??
-      };
+    public:
+      engine::Processor* buildProcessor () ;
+      // TODO: allocation, GC??
+    };
 
 
 
-  } // namespace mobject::controller
-
-} // namespace mobject
+} // namespace control
 #endif
diff --git a/src/proc/mobject/controller/renderstate.hpp b/src/proc/control/renderstate.hpp
similarity index 55%
rename from src/proc/mobject/controller/renderstate.hpp
rename to src/proc/control/renderstate.hpp
index 833e2d347..d62e56628 100644
--- a/src/proc/mobject/controller/renderstate.hpp
+++ b/src/proc/control/renderstate.hpp
@@ -21,38 +21,33 @@
 */
 
 
-#ifndef MOBJECT_CONTROLLER_RENDERSTATE_H
-#define MOBJECT_CONTROLLER_RENDERSTATE_H
+#ifndef CONTROL_RENDERSTATE_H
+#define CONTROL_RENDERSTATE_H
 
 #include "proc/state.hpp"
 
 
 
-namespace mobject
-  {
-  namespace controller
+namespace control {
+  
+  typedef proc_interface::State State;
+  
+  
+  /**
+   * Encapsulates the logic used to get a "current render process"
+   * in accordance to the currently applicable controller settings.
+   * The provided StateProxy serves to hold any mutalbe state used
+   * in the render process, so the rest of the render engine 
+   * can be stateless.
+   * @todo probably the state management will work different (6/08)
+   */
+  class RenderState
     {
-
-    typedef proc_interface::State State;
-
-
-    /**
-     * Encapsulates the logic used to get a "current render process"
-     * in accordance to the currently applicable controller settings.
-     * The provided StateProxy serves to hold any mutalbe state used
-     * in the render process, so the rest of the render engine 
-     * can be stateless.
-     * @todo probably the state management will work different (6/08)
-     */
-    class RenderState
-      {
-      public:
-        State& getRenderProcess () ;
-      };
+    public:
+      State& getRenderProcess () ;
+    };
 
 
 
-  } // namespace mobject::controller
-
-} // namespace mobject
+} // namespace control
 #endif
diff --git a/src/proc/stypemanager.cpp b/src/proc/control/stypemanager.cpp
similarity index 91%
rename from src/proc/stypemanager.cpp
rename to src/proc/control/stypemanager.cpp
index 58eb23944..9c1e60d08 100644
--- a/src/proc/stypemanager.cpp
+++ b/src/proc/control/stypemanager.cpp
@@ -21,13 +21,13 @@
 * *****************************************************/
 
 
-#include "proc/stypemanager.hpp"
+#include "proc/control/stypemanager.hpp"
 
-namespace proc_interface
-  {
+namespace control {
+  
   
   /** */
   
   
   
-} // namespace proc_interface
+} // namespace control
diff --git a/src/proc/stypemanager.hpp b/src/proc/control/stypemanager.hpp
similarity index 89%
rename from src/proc/stypemanager.hpp
rename to src/proc/control/stypemanager.hpp
index d7eeef836..ef99689d5 100644
--- a/src/proc/stypemanager.hpp
+++ b/src/proc/control/stypemanager.hpp
@@ -21,15 +21,15 @@
 */
 
 
-#ifndef PROC_INTERFACE_STATE_H
-#define PROC_INTERFACE_STATE_H
+#ifndef CONTROL_STYPEMANAGER_H
+#define CONTROL_STYPEMANAGER_H
 
 
 #include "common/streamtype.hpp"
 
 
 
-namespace proc_interface {
+namespace control {
   
   using lumiera::Symbol;
   
@@ -45,6 +45,13 @@ namespace proc_interface {
     };
   
   
+} // namespace control
+
+
+namespace proc_interface {
   
+  using control::STypeManager;
+
+
 } // namespace proc_interface
 #endif
diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp
index aac1ff7a1..562767ccc 100644
--- a/src/proc/engine/nodewiringconfig.hpp
+++ b/src/proc/engine/nodewiringconfig.hpp
@@ -19,6 +19,7 @@
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  
 */
+
 /** @file nodewiringconfig.hpp
  ** Sometimes we need to coose a different implementation for dealing with
  ** some special cases. While for simple cases, just testing a flag or using a
@@ -44,6 +45,8 @@
  ** @see configflags.hpp
  ** 
  */
+
+
 #ifndef ENGINE_NODEWIRINGCONFIG_H
 #define ENGINE_NODEWIRINGCONFIG_H
 
diff --git a/src/proc/lumiera.hpp b/src/proc/lumiera.hpp
index 49822b469..f9ce3b838 100644
--- a/src/proc/lumiera.hpp
+++ b/src/proc/lumiera.hpp
@@ -22,6 +22,18 @@
  
 */
 
+/** @file lumiera.hpp
+ ** Basic set of definitions and includes commonly used together.
+ ** Including lumiera.hpp gives you a common set of elementary declarations
+ ** widely used within the C++ code of the Proc-Layer.
+ ** 
+ ** @see main.cpp
+ ** @see pre.hpp
+ ** 
+ */
+
+
+
 #ifndef LUMIERA_H
 #define LUMIERA_H
 
@@ -37,11 +49,67 @@
 #include "lib/appconfig.hpp"
 
 
-namespace lumiera
-  {
-    /* additional global configuration goes here... */
+/**
+ * Namespace for globals.
+ * A small number of definitions and facilities of application wide relevance.
+ * It's probably a good idea to pull it in explicitly and to avoid nesting
+ * implementation namespaces within \c lumiera::
+ */
+namespace lumiera {
+
+  /* additional global configuration goes here... */
   
     
 } // namespace lumiera
 
+
+/**
+ * Namespace for support and library code.
+ */
+namespace lib { 
+
+}
+
+
+/**
+ * The asset subsystem of the Proc-Layer.
+ */
+namespace asset { }
+
+
+/**
+ * Proc-Layer dispatcher, controller and administrative facilities.
+ */
+namespace control { }
+
+
+/**
+ * Render engine code as part of the Proc-Layer.
+ * Backbone of the engine, render nodes base and cooperation.
+ * A good deal of the active engine code is outside the scope of the
+ * Proc-Layer, e.g. code located in backend services and plugins.
+ */
+namespace engine { }
+
+
+
+/**
+ * Media-Objects, edit operations and high-level session.
+ */
+namespace mobject {
+
+
+  /**
+   * Namespace of Session, EDL and user visible high-level objects.
+   */
+  namespace session { }
+
+
+  /**
+   * Namespace of the Builder, transforming high-level into low-level.
+   */
+  namespace builder { }
+
+}
+
 #endif /*LUMIERA_H*/
diff --git a/uml/lumiera/128261 b/uml/lumiera/128261
index f016cf9c4..c4b03ca90 100644
--- a/uml/lumiera/128261
+++ b/uml/lumiera/128261
@@ -1,6 +1,6 @@
 format 40
 "MObject" // ProcessingLayer::MObject
-  revision 31
+  revision 32
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -1189,8 +1189,6 @@ ${inlines}
 
   package_ref 128901 // Builder
 
-  package_ref 129029 // Controller
-
   usecaseview 128261 "config examples"
     //use case diagram settings
     package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default
diff --git a/uml/lumiera/128517 b/uml/lumiera/128517
index 5eed4e170..6feb7ce56 100644
--- a/uml/lumiera/128517
+++ b/uml/lumiera/128517
@@ -1,6 +1,6 @@
 format 40
 "CommonLib" // CommonLib
-  revision 12
+  revision 13
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -26,6 +26,132 @@ format 40
   package_name_in_tab default show_context default show_opaque_action_definition default auto_label_position default write_flow_label_horizontally default draw_all_relations default shadow default
   show_infonote default drawing_language default
   
+  classview 129285 "StreamType"
+    //class diagram settings
+    draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default
+    //collaboration diagram settings
+    show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default
+    //object diagram settings
+     write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default
+    //sequence diagram settings
+    show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default
+    //state diagram settings
+    package_name_in_tab default show_context default auto_label_position default write_trans_label_horizontally default show_trans_definition default draw_all_relations default shadow default
+    show_activities default region_horizontally default drawing_language default
+    //class settings
+    //activity diagram settings
+    package_name_in_tab default show_context default show_opaque_action_definition default auto_label_position default write_flow_label_horizontally default draw_all_relations default shadow default
+    show_infonote default drawing_language default
+    
+    classdiagram 132485 "Stream Type Framework"
+      draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default
+      size A4
+    end
+
+    class 144773 "StreamType"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+      classrelation 158469 // 
+	relation 154373 --->
+	  a role_name "" multiplicity "" protected
+	    cpp default "    ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value};
+"
+	    classrelation_ref 158469 // 
+	  b multiplicity "" parent class_ref 145285 // MediaKind
+      end
+
+      classrelation 158597 // 
+	relation 154501 --->
+	  a role_name "" multiplicity "" protected
+	    cpp default "    ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value};
+"
+	    classrelation_ref 158597 // 
+	  b multiplicity "" parent class_ref 144901 // Prototype
+      end
+
+      classrelation 158725 // 
+	relation 154629 --->
+	  a role_name "" multiplicity "" protected
+	    cpp default "    ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value};
+"
+	    classrelation_ref 158725 // 
+	  b multiplicity "" parent class_ref 145029 // ImplFacade
+      end
+    end
+
+    class 144901 "Prototype"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+    end
+
+    class 145029 "ImplFacade"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+    end
+
+    class 145157 "StreamTypeID"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+      classrelation 158341 // 
+	relation 154245 -_->
+	  a default
+	    cpp default "Generated"
+	    classrelation_ref 158341 // 
+	  b multiplicity "" parent class_ref 144773 // StreamType
+      end
+    end
+
+    class 145285 "MediaKind"
+      visibility package stereotype "enum"
+      cpp_decl "${comment}enum ${name}
+  {
+${items}
+  };
+"
+      java_decl "${comment}${@}${visibility}${final}${abstract}enum ${name}${implements} {
+${items};
+${members}}
+"
+      idl_decl "${comment}enum ${name} {
+${items}};
+"
+      explicit_switch_type ""
+      
+    end
+  end
+
   package_ref 131077 // ConfigQuery
 
   classview 128773 "error"
diff --git a/uml/lumiera/129029 b/uml/lumiera/129029
index 259248b0a..5c7a2a6f4 100644
--- a/uml/lumiera/129029
+++ b/uml/lumiera/129029
@@ -1,6 +1,6 @@
-format 38
-"Controller" // ProcessingLayer::MObject::Controller
-  revision 5
+format 40
+"Control" // ProcessingLayer::Control
+  revision 6
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -8,7 +8,7 @@ format 38
   //use case diagram settings
   package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default
   //sequence diagram settings
-  show_full_operations_definition default write_horizontally default drawing_language default draw_all_relations default shadow default
+  show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default
   //collaboration diagram settings
   show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default
   //object diagram settings
@@ -34,7 +34,7 @@ format 38
     //object diagram settings
      write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default
     //sequence diagram settings
-    show_full_operations_definition default write_horizontally default drawing_language default draw_all_relations default shadow default
+    show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default
     //state diagram settings
     package_name_in_tab default show_context default auto_label_position default write_trans_label_horizontally default show_trans_definition default draw_all_relations default shadow default
     show_activities default region_horizontally default drawing_language default
@@ -172,7 +172,7 @@ ${inlines}
       
       comment "Encapsulates the logic used to get a \"current render process\" in accordance to the currentyl applicable controller settings. The provided StateProxy serves to hold any mutalbe state used in the render process, so the rest of the render engine can be stateless."
       operation 128389 "getStateProxy"
-	public return_type class_ref 132741 // StateProxy
+	public return_type class_ref 132741 // State
 	nparams 0
 	cpp_decl "    ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};"
 	cpp_def "${comment}${inline}${type}
@@ -186,5 +186,18 @@ ${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl}
 	
       end
     end
+
+    class 145413 "STypeManager"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+    end
   end
 end
diff --git a/uml/lumiera/129285 b/uml/lumiera/129285
index 1441c4bdf..4ff722e2d 100644
--- a/uml/lumiera/129285
+++ b/uml/lumiera/129285
@@ -1,6 +1,6 @@
 format 40
 "ProcessingLayer" // ProcessingLayer
-  revision 22
+  revision 23
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -28,6 +28,8 @@ format 40
   
   package_ref 128133 // Asset
 
+  package_ref 129029 // Control
+
   package_ref 128261 // MObject
 
   package_ref 128389 // RenderEngine
diff --git a/uml/lumiera/129413 b/uml/lumiera/129413
index a4a3e7283..34ae1c0c2 100644
--- a/uml/lumiera/129413
+++ b/uml/lumiera/129413
@@ -1,6 +1,6 @@
 format 40
 "common" // design::codegen::common
-  revision 14
+  revision 15
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -201,6 +201,46 @@ ${namespace_end}"
       end
       comment "unified representation of a time point, including conversion functions"
     end
+
+    artifact 139653 "streamtype"
+      stereotype "source"
+      cpp_h "/*
+  ${NAME}.hpp  -  ${description}
+@{CopyrightClaim}@{GPLHeader}
+*/
+
+
+#ifndef ${NAMESPACE}_${NAME}_H
+#define ${NAMESPACE}_${NAME}_H
+
+${includes}
+${declarations}
+
+
+${namespace_start}
+
+${definition}
+${namespace_end}
+#endif
+"
+      cpp_src "/*
+  ${Name}  -  ${description}
+@{CopyrightClaim}@{GPLHeader}
+* *****************************************************/
+
+
+${includes}
+${namespace_start}
+
+
+${members}
+${namespace_end}"
+      associated_classes
+	class_ref 144773 // StreamType
+	class_ref 144901 // Prototype
+	class_ref 145029 // ImplFacade
+      end
+    end
   end
 
   package_ref 130821 // error
diff --git a/uml/lumiera/129669 b/uml/lumiera/129669
index b2c2e0372..fb08fd56f 100644
--- a/uml/lumiera/129669
+++ b/uml/lumiera/129669
@@ -1,6 +1,6 @@
 format 40
 "proc" // design::codegen::proc
-  revision 7
+  revision 8
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -150,7 +150,7 @@ ${namespace_start}
 ${members}
 ${namespace_end}"
       associated_classes
-	class_ref 132741 // StateProxy
+	class_ref 132741 // State
       end
       comment "Key Interface representing a render process and encapsulating state"
     end
@@ -236,6 +236,8 @@ ${namespace_end}"
 
   package_ref 130053 // asset
 
+  package_ref 130693 // control
+
   package_ref 130181 // mobject
 
   package_ref 130309 // engine
diff --git a/uml/lumiera/130181 b/uml/lumiera/130181
index 2204811cd..6d815be5e 100644
--- a/uml/lumiera/130181
+++ b/uml/lumiera/130181
@@ -1,6 +1,6 @@
 format 40
 "mobject" // design::codegen::proc::mobject
-  revision 8
+  revision 9
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -394,6 +394,4 @@ ${namespace_end}"
   package_ref 130437 // session
 
   package_ref 130565 // builder
-
-  package_ref 130693 // controller
 end
diff --git a/uml/lumiera/130693 b/uml/lumiera/130693
index 71f67f571..c4b9c0979 100644
--- a/uml/lumiera/130693
+++ b/uml/lumiera/130693
@@ -1,6 +1,6 @@
 format 40
-"controller" // design::codegen::proc::mobject::controller
-  revision 6
+"control" // design::codegen::proc::control
+  revision 7
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -27,13 +27,13 @@ format 40
   show_infonote default drawing_language default
   
   stereotype "src"
-  cpp_h_dir "proc/mobject/controller"
-  cpp_src_dir "proc/mobject/controller"
-  cpp_namespace "mobject::controller"
+  cpp_h_dir "proc/control"
+  cpp_src_dir "proc/control"
+  cpp_namespace "control"
   comment "sourcecode package
 
 The Processing and Render Controller,
-located within the MObject Subsystem"
+and the Proc-Layer dispatcher"
   deploymentview 129157 "gen"
     //deployment diagram settings
     package_name_in_tab default show_context default write_horizontally default auto_label_position default draw_all_relations default shadow default
@@ -116,5 +116,43 @@ ${namespace_end}"
       end
       comment "renderengine state manager"
     end
+
+    artifact 139781 "stypemanager"
+      stereotype "source"
+      cpp_h "/*
+  ${NAME}.hpp  -  ${description}
+@{CopyrightClaim}@{GPLHeader}
+*/
+
+
+#ifndef ${NAMESPACE}_${NAME}_H
+#define ${NAMESPACE}_${NAME}_H
+
+${includes}
+${declarations}
+
+
+${namespace_start}
+
+${definition}
+${namespace_end}
+#endif
+"
+      cpp_src "/*
+  ${Name}  -  ${description}
+@{CopyrightClaim}@{GPLHeader}
+* *****************************************************/
+
+
+${includes}
+${namespace_start}
+
+
+${members}
+${namespace_end}"
+      associated_classes
+	class_ref 145413 // STypeManager
+      end
+    end
   end
 end
diff --git a/uml/lumiera/131077 b/uml/lumiera/131077
index c996ebb90..480e8aad0 100644
--- a/uml/lumiera/131077
+++ b/uml/lumiera/131077
@@ -1,6 +1,6 @@
 format 40
 "ConfigQuery" // CommonLib::ConfigQuery
-  revision 1
+  revision 2
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -239,7 +239,7 @@ ${inlines}
       classrelation 150405 // 
 	relation 147717 -_->
 	  a default
-	    cpp default "Generated"
+	    cpp default "#include in header"
 	    classrelation_ref 150405 // 
 	  b multiplicity "" parent class_ref 140805 // TypeHandler
       end
@@ -390,7 +390,7 @@ ${inlines}
       classrelation 150533 // 
 	relation 147845 -_->
 	  a default
-	    cpp default "Generated"
+	    cpp default "#include in header"
 	    classrelation_ref 150533 // 
 	  b multiplicity "" parent class_ref 140549 // ConfigRules
       end
diff --git a/uml/lumiera/132485.diagram b/uml/lumiera/132485.diagram
new file mode 100644
index 000000000..ac0affa9e
--- /dev/null
+++ b/uml/lumiera/132485.diagram
@@ -0,0 +1,49 @@
+format 40
+
+classcanvas 128005 class_ref 144773 // StreamType
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default
+  xyz 188 72 2000
+  end
+classcanvas 128133 class_ref 144901 // Prototype
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default
+  xyz 261 208 2000
+  end
+classcanvas 128261 class_ref 145029 // ImplFacade
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default
+  xyz 261 271 2000
+  end
+classcanvas 128389 class_ref 145157 // StreamTypeID
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default
+  xyz 38 72 2000
+  end
+classcanvas 128645 class_ref 145285 // MediaKind
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default
+  xyz 263 132 2000
+  end
+classcanvas 129541 class_ref 145413 // STypeManager
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default
+  xyz 40 256 2000
+  end
+relationcanvas 128517 relation_ref 154245 // 
+  from ref 128389 z 1999 to ref 128005
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+relationcanvas 128773 relation_ref 154373 // 
+  geometry VH
+  from ref 128005 z 1999 to point 223 162
+  line 128901 z 1999 to ref 128645
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+relationcanvas 129029 relation_ref 154501 // 
+  geometry VH
+  from ref 128005 z 1999 to point 223 227
+  line 129157 z 1999 to ref 128133
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+relationcanvas 129285 relation_ref 154629 // 
+  geometry VH
+  from ref 128005 z 1999 to point 223 290
+  line 129413 z 1999 to ref 128261
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session
index 3de151827..af68f0264 100644
--- a/uml/lumiera/5.session
+++ b/uml/lumiera/5.session
@@ -4,16 +4,21 @@ diagrams
     407 690 100 4 2 0
   collaborationdiagram_ref 132229 // Render Process
     825 684 100 4 0 0
-  active  classdiagram_ref 132357 // StateAdapter composition
+  classdiagram_ref 132357 // StateAdapter composition
     414 658 100 4 0 0
+  active  classdiagram_ref 132485 // Stream Type Framework
+    463 594 100 4 0 0
 end
 show_stereotypes
 selected 
-package_ref 129 // lumiera
+  package_ref 129 // lumiera
 open
-  class_ref 132741 // State
+  deploymentview_ref 128261 // gen
+  deploymentview_ref 129157 // gen
   
-package_ref 129029 // Controller
+  package_ref 130181 // mobject
+  classview_ref 128389 // Controller Workings
+  class_ref 132741 // State
   class_ref 131717 // ProcNode
   class_ref 142469 // StateProxy
   class_ref 142597 // StateAdapter
@@ -28,11 +33,7 @@ package_ref 129029 // Controller
   class_ref 144005 // BuffTable
   class_ref 144261 // Invocation
   usecaseview_ref 128005 // Renderengine Use
-  class_ref 140677 // QueryHandler
-  class_ref 140805 // TypeHandler
-  class_ref 140933 // ResolverBase
-  class_ref 141189 // QueryHandlerImpl
-  usecaseview_ref 128389 // query use
+  classview_ref 129285 // StreamType
   classview_ref 128773 // error
   class_ref 140165 // Visitable
 end

From 7ed7f05ffbc759702b766ff560daad22a49a947d Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 13 Sep 2008 06:00:22 +0200
Subject: [PATCH 03/66] further analyzing the problem (stream type handling)

---
 src/common/streamtype.hpp      | 16 ++++++++++++++-
 src/proc/engine/buffhandle.hpp |  3 ++-
 wiki/renderengine.html         | 37 +++++++++++++++++++++++++++++-----
 3 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp
index 86bf9a6bf..b4ec84b2a 100644
--- a/src/common/streamtype.hpp
+++ b/src/common/streamtype.hpp
@@ -73,7 +73,7 @@ namespace lumiera
       MediaKind kind;
       Prototype const& prototype;
       ImplFacade * implType;
-      Usage usageTag;
+      Usage intentionTag;
       
     };
   
@@ -91,6 +91,12 @@ namespace lumiera
     };
   
   
+
+  /** 
+   * placeholder definition for the contents of a data buffer
+   */
+  struct DataBuffer { };
+  
   
   /**
    * 
@@ -100,6 +106,14 @@ namespace lumiera
     public:
       Symbol libraryID;
       
+      bool operator== (ImplFacade const& other)  const;
+      bool operator== (StreamType const& other)  const;
+      
+      bool canConvert (ImplFacade const& other)  const;
+      bool canConvert (StreamType const& other)  const;
+      
+      DataBuffer* createFrame ()  const;
+      
     };
     
   
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 51ce1c135..f55277e7a 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -40,6 +40,7 @@
 
 
 #include "common/error.hpp"
+#include "common/streamtype.hpp"
 
 
 namespace engine {
@@ -52,7 +53,7 @@ namespace engine {
    */
   struct BuffHandle
     {
-      typedef float Buff;
+      typedef lumiera::DataBuffer Buff;
       typedef Buff* PBuff;//////TODO define the Buffer type(s)
       
       PBuff 
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index d5d2bda01..905e69369 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1365,7 +1365,7 @@ Besides routing to a global pipe, wiring plugs can also connect to the source po
 Finally, this example shows an ''automation'' data set controlling some parameter of an effect contained in one of the global pipes. From the effect's POV, the automation is simply a ParamProvider, i.e a function yielding a scalar value over time. The automation data set may be implemented as a bézier curve, or by a mathematical function (e.g. sine or fractal pseudo random) or by some captured and interpolated data values. Interestingly, in this example the automation data set has been placed relatively to the meta clip (albeit on another track), thus it will follow and adjust when the latter is moved.
 
-
+
This wiki page is the entry point to detail notes covering some technical decisions, details and problems encountered in the course of the implementation of the Lumiera Renderengine, the Builder and the related parts.
 
 * [[Packages, Interfaces and Namespaces|InterfaceNamespaces]]
@@ -1383,7 +1383,7 @@ Finally, this example shows an ''automation'' data set controlling some paramete
 * [[identifying the basic Builder operations|BasicBuildingOperations]] and [[planning the Implementation|PlanningNodeCreatorTool]]
 * [[how to handle »attached placement«|AttachedPlacementProblem]]
 * working out the [[basic building situations|BuilderPrimitives]] and [[mechanics of rendering|RenderMechanics]]
-* how to classify and [[describe media stream types|StreamType]]
+* how to classify and [[describe media stream types|StreamType]] and how to [[use them|StreamTypeUse]]
 
@@ -3326,7 +3326,7 @@ Consequently, as we can't get away with an fixed Enum of all stream prototypes, NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus linear PCM...
-
+
//how to classify and describe media streams//
 Media data is understood to appear structured as stream(s) over time. While there may be an inherent internal structuring, at a given perspective ''any stream is a unit and homogeneous''. In the context of digital media data processing, streams are always ''quantized'', which means they appear as a temporal sequence of data chunks called ''frames''.
 
@@ -3357,15 +3357,16 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu
 * determine if a given media data source and sink can be connected, and how.
 * determine and enumerate the internal structure of a stream.
 * discover processing facilities
-
+&rarr; see StreamTypeUse
-
+
A description and classification record usable to find out about the properties of a media stream. The stream type descriptor can be accessed using an unique StreamTypeID. The information contained in this descriptor record can intentionally be //incomplete,// in which case the descriptor captures a class of matching media stream types. The following information is maintained:
 * fundamental ''kind'' of media: {{{VIDEO, IMAGE, AUDIO, MIDI,...}}}
 * stream ''prototype'': this is the abstract high level media type, like NTSC, PAL, Film, 3D, Ambisonics, 5.1, monaural,...
 * stream ''implementation type'' accessible by virtue of an StreamTypeImplFacade
 * the ''intended usage category'' of this stream: {{{SOURCE, RAW, INTERMEDIARY, TARGET}}}.
 &rarr; see &raquo;[[Stream Type|StreamType]]&laquo; detailed specification
+&rarr; notes about [[using stream types|StreamTypeUse]]
 &rarr; more [[about prototypes|StreamPrototype]]
@@ -3379,6 +3380,32 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu * ...? &rarr; see also &raquo;[[Stream Type|StreamType]]&laquo; + +
+
+
Questions regarding the use of StreamType within the Proc-Layer.
+
+* what is the relation between Buffer and Frame?
+* how to get the required size of a Buffer?
+* who does buffer allocations and how?
+
+!creating stream types
+seemingly stream types are created based on an already existing media stream (or a Frame of media data?). {{red{really?}}}
+The other use case seems to be that of an //incomplete// stream type based on a [[Prototype|StreamPrototype]]
+
+!Prototype
+According to my current understanding, a prototype is merely classification entity. But then &mdash; how to bootstrap a Prototype?
+And how to do the classification of an existing implementation type
+
+!the ID problem
+Basically I'd prefer the ~IDs to be real identifiers. So they can be used directly within rules. At least the Prototypes //can// have such a textual identifier. But the implementation type is problematic, and consequently the ID of the StreamType as well. Because the actual implementation should not be nailed down to a fixed set of possibilities. And, generally, we can't expect an implementation library to yield textual identifiers for each implementation type. //Is this really a problem? {{red{what are the use cases?}}}//
+
+--------------
+!use cases
+* pulling from a media file
+* connecting pipes and similar wiring problems
+* describing the properties of an processor plugin
+
 
From 8754555492dece52e329258375c9af4e1922f79b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 15 Sep 2008 05:40:13 +0200 Subject: [PATCH 04/66] further mulling over the problems related to stream type handling --- src/common/streamtype.hpp | 43 +++++++++++++++++++++++++++++++++------ wiki/renderengine.html | 11 ++++++++-- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp index b4ec84b2a..e419dbfc7 100644 --- a/src/common/streamtype.hpp +++ b/src/common/streamtype.hpp @@ -68,6 +68,7 @@ namespace lumiera struct Prototype; class ImplFacade; + class ImplConstraint; MediaKind kind; @@ -99,20 +100,50 @@ namespace lumiera /** - * + * A (more or less) concrete implementation type, wired up + * as a facade providing the basic set of operations. */ class StreamType::ImplFacade { public: Symbol libraryID; - bool operator== (ImplFacade const& other) const; - bool operator== (StreamType const& other) const; + virtual bool operator== (ImplFacade const& other) const; + virtual bool operator== (StreamType const& other) const; - bool canConvert (ImplFacade const& other) const; - bool canConvert (StreamType const& other) const; + virtual bool canConvert (ImplFacade const& other) const; + virtual bool canConvert (StreamType const& other) const; - DataBuffer* createFrame () const; + virtual DataBuffer* createFrame () const; + + }; + + + /** + * + */ + class StreamType::ImplConstraint + : public StreamType::ImplFacade + { + public: + Symbol libraryID; + + virtual bool canConvert (ImplFacade const& other) const; + virtual bool canConvert (StreamType const& other) const; + + virtual bool subsumes (ImplFacade const& other) const; + + /** modify the other impl type such as to comply with this constraint */ + virtual void makeCompliant (ImplFacade & other) const; + + /** create a default impl type in accordance to this constraint + * and use it to create a new framebuffer */ + virtual DataBuffer* createFrame () const; + + /** similarily create a impl type which complies to this constraint + * as well as to the additional constraints (e.g. frame size). + * Create a new framebuffer of the resutling type */ + virtual DataBuffer* createFrame (ImplConstraint const& furtherConstraints) const; }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 905e69369..2e20400b1 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3382,7 +3382,7 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu &rarr; see also &raquo;[[Stream Type|StreamType]]&laquo;
-
+
Questions regarding the use of StreamType within the Proc-Layer.
 
 * what is the relation between Buffer and Frame?
@@ -3394,11 +3394,18 @@ seemingly stream types are created based on an already existing media stream (or
 The other use case seems to be that of an //incomplete// stream type based on a [[Prototype|StreamPrototype]]
 
 !Prototype
-According to my current understanding, a prototype is merely classification entity. But then &mdash; how to bootstrap a Prototype?
+According to my current understanding, a prototype is merely a classification entity. But then &mdash; how to bootstrap a Prototype?
 And how to do the classification of an existing implementation type
 
+!Defaults and partial specification
+A StreamType need not be completely defined. It is sufficient to specify the media kind and the Prototype. The implementation type may be just given as a constraint, thus defining some properties and leaving out others. When creating a frame buffer based upon such an //incomplete type,// [[defaults|DefaultsManagement]] are queried to fill in the missing parts.
+Constraints are objects provided by the Lumiera core, but specialized to the internals of the actual implementation library.
+For example there might be a constraint implementation to force a specific {{{gavl_pixelformat_t}}}.
+
 !the ID problem
 Basically I'd prefer the ~IDs to be real identifiers. So they can be used directly within rules. At least the Prototypes //can// have such a textual identifier. But the implementation type is problematic, and consequently the ID of the StreamType as well. Because the actual implementation should not be nailed down to a fixed set of possibilities. And, generally, we can't expect an implementation library to yield textual identifiers for each implementation type. //Is this really a problem? {{red{what are the use cases?}}}//
+As far as I can see, in most cases this is no problem, as the type can be retrieved or derived from an existing media object. Thus, the only problematic case is when we need to persist the type information without being able to guarantee the persistence of the media object this type was derived from. For example this might be a problem when working with proxy media. But at least we should be able to create a constraint (partial type specification) to cover the important part of the type information, i.e. the part which is needed to re-create the model even when the original media isn't there any longer.
+Thus, //constraints may be viewed as type constructing functors.//
 
 --------------
 !use cases

From a6a19ef60929206fee06b8c7618d5b91ebb27140 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 17 Sep 2008 03:46:38 +0200
Subject: [PATCH 05/66] WIP start drafting how to bootstrap a stream type...

---
 src/common/streamtype.cpp             |  4 +-
 src/common/streamtype.hpp             |  4 +-
 tests/50components.tests              |  4 ++
 tests/common/streamtypebasicstest.cpp | 67 +++++++++++++++++++++++++++
 tests/common/teststreamtypes.hpp      | 52 +++++++++++++++++++++
 wiki/renderengine.html                | 16 ++++++-
 6 files changed, 142 insertions(+), 5 deletions(-)
 create mode 100644 tests/common/streamtypebasicstest.cpp
 create mode 100644 tests/common/teststreamtypes.hpp

diff --git a/src/common/streamtype.cpp b/src/common/streamtype.cpp
index a230ba7d4..e236148e9 100644
--- a/src/common/streamtype.cpp
+++ b/src/common/streamtype.cpp
@@ -28,7 +28,9 @@ namespace lumiera
   {
 
    /** */
-   int bla = 3;
+   StreamType::ImplFacade::~ImplFacade() 
+   { }
+
   
 
 } // namespace lumiera
diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp
index e419dbfc7..40f8b1214 100644
--- a/src/common/streamtype.hpp
+++ b/src/common/streamtype.hpp
@@ -40,8 +40,7 @@
 #include 
 
 
-namespace lumiera
-  {
+namespace lumiera {
 
 
   /**
@@ -116,6 +115,7 @@ namespace lumiera
       
       virtual DataBuffer* createFrame ()  const;
       
+      virtual ~ImplFacade() ;
     };
   
   
diff --git a/tests/50components.tests b/tests/50components.tests
index 417b094ab..57534f06a 100644
--- a/tests/50components.tests
+++ b/tests/50components.tests
@@ -280,6 +280,10 @@ out: dtor ~TargetObj(24) successfull
 END
 
 
+PLANNED "StreamTypeBasics_test" StreamTypeBasics_test < Testgroup=ALL
diff --git a/tests/common/streamtypebasicstest.cpp b/tests/common/streamtypebasicstest.cpp
new file mode 100644
index 000000000..96989f2bc
--- /dev/null
+++ b/tests/common/streamtypebasicstest.cpp
@@ -0,0 +1,67 @@
+/*
+  StreamTypeBasics(Test)  -  check the fundamentals of stream type information
+ 
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+ 
+  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 "common/test/run.hpp"
+#include "common/util.hpp"
+
+#include "teststreamtypes.hpp"
+
+#include 
+using std::cout;
+
+
+
+namespace lumiera {
+  namespace test {
+    
+
+    /*******************************************************************
+     * @test check the basic workings of the stream type handling.
+     *       create some stream implementation data, build a 
+     *       StreamType::ImplFacade from this, and derive a prototype
+     *       and a full StreamType based on this information.
+     */
+    class StreamTypeBasics_test : public Test
+      {
+        virtual void run (Arg arg)
+          {
+            buildImplType ();
+          }
+        
+        void buildImplType ()
+          {
+            StreamType::ImplFacade iType = createImplType ();
+            
+            UNIMPLEMENTED ("get a lib descriptor"); 
+            UNIMPLEMENTED ("check the lib of the type"); 
+            UNIMPLEMENTED ("compare two types"); 
+          }
+      };
+    
+      LAUNCHER (StreamTypeBasics_test, "unit common");
+
+      
+  } // namespace test
+    
+} // namespace lumiera
+
diff --git a/tests/common/teststreamtypes.hpp b/tests/common/teststreamtypes.hpp
new file mode 100644
index 000000000..e812b78b0
--- /dev/null
+++ b/tests/common/teststreamtypes.hpp
@@ -0,0 +1,52 @@
+/*
+  TESTSTREAMTYPES.hpp  -  create test (stub) stream type information
+ 
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+ 
+  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 LUMIERA_TEST_TESTSTREAMTYPES_H
+#define LUMIERA_TEST_TESTSTREAMTYPES_H
+
+
+//#include "common/util.hpp"
+
+#include "common/streamtype.hpp"
+#include "proc/control/stypemanager.hpp"
+
+
+
+namespace lumiera {
+  namespace test {
+    
+    /** Helper: create an implementation frame
+     *  and build the corresponding streamtype
+     */
+    inline StreamType::ImplFacade
+    createImplType ()
+    {
+      UNIMPLEMENTED ("create a test stream type from a given GAVL type tag");
+    }
+    
+    
+    
+  } // namespace test
+
+} // namespace lumiera
+#endif
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 2e20400b1..851b8a8e2 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -3382,7 +3382,7 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu
 &rarr; see also &raquo;[[Stream Type|StreamType]]&laquo;
 
-
+
Questions regarding the use of StreamType within the Proc-Layer.
 
 * what is the relation between Buffer and Frame?
@@ -3409,10 +3409,22 @@ Thus, //constraints may be viewed as type constructing functors.//
 
 --------------
 !use cases
-* pulling from a media file
+* pulling data from a media file
 * connecting pipes and similar wiring problems
 * describing the properties of an processor plugin
 
+!! pulling data from a media file
+To open the file, we need //type discovery code,// resulting in a handle to some library module for accessing the contents, which is in compliance with the Lumiera application. Thus, we can determine the possible return values of this type discovery code and provide code which wires up a corresponding StreamTypeImpleFacade. Further, the {{{control::STypeManager}}} has the ability to build a complete or partial StreamType from
+* an ~ImplFacade
+* a Prototype
+* maybe even from some generic textual IDs?
+
+!! wiring problems
+When deciding if a connection can be made, we can build up the type information starting out from the source. (this requires some work, but it's //possible,// generally speaking.). Thus, we can allways get an ~ImplType for the "lower end" of the connection, and at least a Prototype for the "output side" &mdash; which should be enough to use the query functions provided by the stream type interfaces
+
+!! describing properties
+{{red{currently difficult to define}}} as of 9/2008, because the property description of plugins is not planned yet.
+My Idea was to use [[type implementation constraints|StreanTyoeImplConstraint]] for this, which are a special kind of ~ImplType
 
From 2885b41895737af755cf36c9a5e9fb18a3b43717 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 22 Sep 2008 04:03:37 +0200 Subject: [PATCH 06/66] fix a link problem --- src/common/streamtype.cpp | 7 ++++--- src/common/streamtype.hpp | 27 +++++++++++++++------------ tests/common/streamtypebasicstest.cpp | 2 +- tests/common/teststreamtypes.hpp | 2 +- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/common/streamtype.cpp b/src/common/streamtype.cpp index e236148e9..b6b1d3e0c 100644 --- a/src/common/streamtype.cpp +++ b/src/common/streamtype.cpp @@ -27,9 +27,10 @@ namespace lumiera { - /** */ - StreamType::ImplFacade::~ImplFacade() - { } + /** @internal defined here non-inline place the vtable in this object file.*/ + StreamType::ImplFacade::ImplFacade (Symbol libID) + : libraryID(libID) + { } diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp index 40f8b1214..8fb50460b 100644 --- a/src/common/streamtype.hpp +++ b/src/common/streamtype.hpp @@ -107,15 +107,18 @@ namespace lumiera { public: Symbol libraryID; - virtual bool operator== (ImplFacade const& other) const; - virtual bool operator== (StreamType const& other) const; + virtual bool operator== (ImplFacade const& other) const =0; + virtual bool operator== (StreamType const& other) const =0; - virtual bool canConvert (ImplFacade const& other) const; - virtual bool canConvert (StreamType const& other) const; + virtual bool canConvert (ImplFacade const& other) const =0; + virtual bool canConvert (StreamType const& other) const =0; - virtual DataBuffer* createFrame () const; + virtual DataBuffer* createFrame () const =0; - virtual ~ImplFacade() ; + virtual ~ImplFacade() {}; + + protected: + ImplFacade (Symbol libID) ; }; @@ -128,22 +131,22 @@ namespace lumiera { public: Symbol libraryID; - virtual bool canConvert (ImplFacade const& other) const; - virtual bool canConvert (StreamType const& other) const; + virtual bool canConvert (ImplFacade const& other) const =0; + virtual bool canConvert (StreamType const& other) const =0; - virtual bool subsumes (ImplFacade const& other) const; + virtual bool subsumes (ImplFacade const& other) const =0; /** modify the other impl type such as to comply with this constraint */ - virtual void makeCompliant (ImplFacade & other) const; + virtual void makeCompliant (ImplFacade & other) const =0; /** create a default impl type in accordance to this constraint * and use it to create a new framebuffer */ - virtual DataBuffer* createFrame () const; + virtual DataBuffer* createFrame () const =0; /** similarily create a impl type which complies to this constraint * as well as to the additional constraints (e.g. frame size). * Create a new framebuffer of the resutling type */ - virtual DataBuffer* createFrame (ImplConstraint const& furtherConstraints) const; + virtual DataBuffer* createFrame (ImplConstraint const& furtherConstraints) const =0; }; diff --git a/tests/common/streamtypebasicstest.cpp b/tests/common/streamtypebasicstest.cpp index 96989f2bc..f8eacf15f 100644 --- a/tests/common/streamtypebasicstest.cpp +++ b/tests/common/streamtypebasicstest.cpp @@ -50,7 +50,7 @@ namespace lumiera { void buildImplType () { - StreamType::ImplFacade iType = createImplType (); + StreamType::ImplFacade& iType = createImplType (); UNIMPLEMENTED ("get a lib descriptor"); UNIMPLEMENTED ("check the lib of the type"); diff --git a/tests/common/teststreamtypes.hpp b/tests/common/teststreamtypes.hpp index e812b78b0..03409c7a9 100644 --- a/tests/common/teststreamtypes.hpp +++ b/tests/common/teststreamtypes.hpp @@ -38,7 +38,7 @@ namespace lumiera { /** Helper: create an implementation frame * and build the corresponding streamtype */ - inline StreamType::ImplFacade + inline StreamType::ImplFacade& createImplType () { UNIMPLEMENTED ("create a test stream type from a given GAVL type tag"); From 578178a937679e57cc88ab4dc7b2d93c69cebf90 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 22 Sep 2008 06:42:10 +0200 Subject: [PATCH 07/66] brainstorming (continued). MediaImplLib considering how to snap in the actual implementation --- src/common/streamtype.hpp | 14 +++-- src/lib/external/libgavl.cpp | 58 ++++++++++++++++++++ src/lib/external/libgavl.hpp | 89 +++++++++++++++++++++++++++++++ src/proc/control/mediaimpllib.hpp | 54 +++++++++++++++++++ src/proc/control/stypemanager.hpp | 3 ++ src/proc/engine/buffhandle.hpp | 2 +- wiki/renderengine.html | 37 +++++++++++-- 7 files changed, 247 insertions(+), 10 deletions(-) create mode 100644 src/lib/external/libgavl.cpp create mode 100644 src/lib/external/libgavl.hpp create mode 100644 src/proc/control/mediaimpllib.hpp diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp index 8fb50460b..6319c40a5 100644 --- a/src/common/streamtype.hpp +++ b/src/common/streamtype.hpp @@ -92,11 +92,6 @@ namespace lumiera { - /** - * placeholder definition for the contents of a data buffer - */ - struct DataBuffer { }; - /** * A (more or less) concrete implementation type, wired up @@ -107,6 +102,13 @@ namespace lumiera { public: Symbol libraryID; + /** placeholder definition for implementation specific type information */ + struct TypeTag { }; + + /** placeholder definition for the contents of a data buffer */ + struct DataBuffer { }; + + virtual bool operator== (ImplFacade const& other) const =0; virtual bool operator== (StreamType const& other) const =0; @@ -148,6 +150,8 @@ namespace lumiera { * Create a new framebuffer of the resutling type */ virtual DataBuffer* createFrame (ImplConstraint const& furtherConstraints) const =0; + //TODO: do we need functions to represent and describe this constraint? + }; diff --git a/src/lib/external/libgavl.cpp b/src/lib/external/libgavl.cpp new file mode 100644 index 000000000..7b477c871 --- /dev/null +++ b/src/lib/external/libgavl.cpp @@ -0,0 +1,58 @@ +/* + STypeManager - entry point for dealing with media stream types + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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/lumiera.hpp" +#include "lib/external/libgavl.hpp" + +extern "C" { +#include +} + + +namespace lib { + namespace external { + + namespace { // implementation internals + + } + + /** + * Use an type information struct, which actually has to be + * a GAVL frametype (TODO), to wire up an ImplFacade such + * as to deal with GAVL data frames of this type. + * @todo fill in the acutal GAVL frame type + * @todo how to distinguish the audio and the video case? + */ + ImplFacadeGAVL const& + LibGavl::getImplFacade (TypeTag*) + { + TODO ("any chance to verify that the TypeTag actually points to a GAVL frame type descriptor?"); + UNIMPLEMENTED ("wire up an impl facade with the correct GAVL lib functions for the data type in question"); + } + + + + + } // namespace external + +} // namespace lib diff --git a/src/lib/external/libgavl.hpp b/src/lib/external/libgavl.hpp new file mode 100644 index 000000000..26f6b6237 --- /dev/null +++ b/src/lib/external/libgavl.hpp @@ -0,0 +1,89 @@ +/* + LILBGAVL.hpp - facade for integrating the GAVL media handling library + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 LIB_EXTERN_LIBGAVL_H +#define LIB_EXTERN_LIBGAVL_H + + +#include "proc/control/mediaimpllib.hpp" + + + +namespace lib { + namespace external { + + + using lumiera::Symbol; + + using lumiera::StreamType; + typedef StreamType::ImplFacade ImplFacade; + typedef StreamType::ImplFacade::TypeTag TypeTag; + + + class LibGavl; + + /** + * Concrete media lib implementation facade + * allowing to work with GAVL data frames and types + * in an implementation agnostic way + */ + class ImplFacadeGAVL + : public ImplFacade + { + protected: + ImplFacadeGAVL() + : ImplFacade("GAVL") + { } + + friend class LibGavl; + + public: + virtual bool operator== (ImplFacade const& other) const; + virtual bool operator== (StreamType const& other) const; + + virtual bool canConvert (ImplFacade const& other) const; + virtual bool canConvert (StreamType const& other) const; + + virtual DataBuffer* createFrame () const; + }; + + + class LibGavl + : public control::MediaImplLib + { + protected: + + public: + virtual Symbol getLibID() const { return "GAVL"; } + + virtual ImplFacadeGAVL const& getImplFacade (TypeTag*); + }; + + + ////////////////////////////////////TODO: dafür sorgen, daß sich das beim Systemstart in den control::STypeManger einklinkt!!!!!!!! + + + } // namespace external + +} // namespace lib +#endif diff --git a/src/proc/control/mediaimpllib.hpp b/src/proc/control/mediaimpllib.hpp new file mode 100644 index 000000000..c150ad913 --- /dev/null +++ b/src/proc/control/mediaimpllib.hpp @@ -0,0 +1,54 @@ +/* + MEDIAIMPLLIB.hpp - interface providing a facade to an media handling library + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 CONTROL_MEDIAIMPLLIB_H +#define CONTROL_MEDIAIMPLLIB_H + + +#include "common/streamtype.hpp" + + + +namespace control { + + using lumiera::Symbol; + + + class MediaImplLib + { + protected: + virtual ~MediaImplLib() {}; + + typedef lumiera::StreamType::ImplFacade ImplFacade; + typedef lumiera::StreamType::ImplFacade::TypeTag TypeTag; + typedef lumiera::StreamType::ImplFacade::DataBuffer DataBuffer; + + public: + virtual Symbol getLibID() const =0; + + virtual ImplFacade const& getImplFacade (TypeTag*) =0; + }; + + +} // namespace control +#endif diff --git a/src/proc/control/stypemanager.hpp b/src/proc/control/stypemanager.hpp index ef99689d5..1a5a7ec96 100644 --- a/src/proc/control/stypemanager.hpp +++ b/src/proc/control/stypemanager.hpp @@ -42,6 +42,9 @@ namespace control { public: lumiera::StreamType const& getType (Symbol sTypeID) ; + + + ////////////////TODO: Mechanismus erfinden, mit dem sich MediaImplLib-Instanzen einklinken können.... }; diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index f55277e7a..069b9f83e 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -53,7 +53,7 @@ namespace engine { */ struct BuffHandle { - typedef lumiera::DataBuffer Buff; + typedef lumiera::StreamType::ImplFacade::DataBuffer Buff; typedef Buff* PBuff;//////TODO define the Buffer type(s) PBuff diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 851b8a8e2..893bf34ea 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1320,6 +1320,11 @@ Slider.prototype.stop = function() } //}}}
+
+
The ''Gmerlin Audio Video Library''. &rarr; see [[homepage|http://gmerlin.sourceforge.net/gavl.html]]
+Used within Lumiera as a foundation for working with raw video and audio media data
+
+
__G__roup __of__ __P__ictures: several compressed video formats don't encode single frames. Normally, such formats are considered mere //delivery formates// but it was one of the key strenghts of Cinelrra from start to be able to do real non linear editing on such formats (like the ~MPEG2-ts unsed in HDV video). The problem of course is that the data backend needs to decode the whole GOP to be serve  single raw video frames.
 
@@ -1832,6 +1837,21 @@ __5/2008__: the allocation mechanism can surely be improved later, but for now I
 &rarr; see also LoadingMedia
 
+
+
The Proc-Layer is designed such as to avoid unnecessary assumptions regarding the properties of the media data and streams. Thus, for anything which is not completely generic, we rely on an abstract [[type description|StreamTypeDescriptor]], which provides a ''Facade'' to an actual library implementation. This way, the fundamental operations can be invoked, like allocating a buffer to hold media data.
+
+In the context of Lumiera and especially in the Proc-Layer, __media implementation library__ means
+* a subsystem which allows to work with media data of a specific kind
+* such as to provide the minimal set of operations
+** allocating a frame buffer
+** describing the type of the data within such a buffer
+* and this subsystem or external library has been integrated to be used by Lumiera by writing adaptation code for accessing these basic operations through the [[implementation facade interface|StreamTypeImplFacade]]
+* such a link to an type implementation is registered and maintained by the [[stream type manager|STypeManager]]
+
+!Problem of the implementation data types
+Because we deliberately won't make any asumptions about the implementation library (besides the ones imposed indirectly by the facade interface), we can't integrate the data types of the library first class into the type system. All we can do is to use marker types and rely on the builder to have checked the compatibility of the actual data beforehand.
+
+
Of course: Cinelerra currently leaks memory and crashes regularilly. For the newly written code, besides retaining the same level of performance, a main goal is to use methods and techniques known to support the writing of quality code. So, besides the MultithreadConsiderations, a solid strategy for managing the ownership of allocated memory blocks is necessary right from start.
 
@@ -3196,6 +3216,9 @@ __see also__
 
 Rendering can be seen as a passive service available to the Backend, which remains in charge what to render and when. Render processes may be running in parallel without any limitations. All of the storage and data management falls into the realm of the Backend. The render nodes themselves are ''completely stateless'' &mdash; if some state is necessary for carrying out the calculations, the backend will provide a //state frame// in addition to the data frames.
+
+
A facility allowing the Proc-Layer to work with abstracted [[media stream types|StreamType]], linking (abstract or opaque) [[type tags|StreamTypeDescriptor]] to an [[library|MediaImplLib]], which provides functionality for acutally dealing with data of this media stream type. Thus, the stream type manager is a kind of registry of all the external libraries which can be bridged and accessed by Lumiera (for working with media data, that is). The most basic set of libraries is instelled here automatically at application start, most notably the [[GAVL]] library for working with uncompressed video and audio data. //Later on, when plugins will introduce further external libraries, these need to be registered here too.//
+
The Session contains all informations, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[EDL (Edit Decision List)|EDL]]. &rarr; [[Session design overview|SessionOverview]]
 
@@ -3372,6 +3395,12 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu
 
This ID is an symbolic key linked to a StreamTypeDescriptor. The predicate {{{stream(ID)}}} specifies a media stream with the StreamType as detailed by the corresponding descriptor (which may contain complete or partial data defining the type).
+
+
A special kind of media stream [[implementation type|StreamTypeImplFacade]], which is not fully specified. As such, it is supposed there //actually is// an concrete implementation type, while only caring for some part or detail of this implementation to exhibit a specific property. For example, using an type constraint we can express the requirement of the actual implementation of a video stream to be based on RGB-float, or to enforce a fixed frame size in pixels.
+
+An implementation constraint can //stand-in// for a completely specified implementation type (meaning it's a sub interface of the latter). But actually using it in this way may cause a call to the [[defaults manager|DefaultsImplementation]] to fill in any missing information. An example would be to call {{{createFrame()}}} on the type constraint object, which means being able to allocate memory to hold a data frame, with properties in compliance with the given type constraint. Of cousre, then we need to know all the properties of this stream type, which is where the defaults manager is queried. This allows session customisation to kick in, but may fail under certain cicumstances.
+
+
Common interface for dealing with the implementation of media stream data. From a high level perspective, the various kinds of media ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}) exhibit similar behaviour, while on the implementation level not even the common classification can be settled down to a complete general and useful scheme. Thus, we need separate library implementations for deailing with the various sorts of media data, all providing at least a set of basic operations:
 * set up a buffer
@@ -3382,7 +3411,7 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu
 &rarr; see also &raquo;[[Stream Type|StreamType]]&laquo;
 
-
+
Questions regarding the use of StreamType within the Proc-Layer.
 
 * what is the relation between Buffer and Frame?
@@ -3414,17 +3443,17 @@ Thus, //constraints may be viewed as type constructing functors.//
 * describing the properties of an processor plugin
 
 !! pulling data from a media file
-To open the file, we need //type discovery code,// resulting in a handle to some library module for accessing the contents, which is in compliance with the Lumiera application. Thus, we can determine the possible return values of this type discovery code and provide code which wires up a corresponding StreamTypeImpleFacade. Further, the {{{control::STypeManager}}} has the ability to build a complete or partial StreamType from
+To open the file, we need //type discovery code,// resulting in a handle to some library module for accessing the contents, which is in compliance with the Lumiera application. Thus, we can determine the possible return values of this type discovery code and provide code which wires up a corresponding StreamTypeImplFacade. Further, the {{{control::STypeManager}}} has the ability to build a complete or partial StreamType from
 * an ~ImplFacade
 * a Prototype
-* maybe even from some generic textual IDs?
+* maybe even from some generic textual ~IDs?
 
 !! wiring problems
 When deciding if a connection can be made, we can build up the type information starting out from the source. (this requires some work, but it's //possible,// generally speaking.). Thus, we can allways get an ~ImplType for the "lower end" of the connection, and at least a Prototype for the "output side" &mdash; which should be enough to use the query functions provided by the stream type interfaces
 
 !! describing properties
 {{red{currently difficult to define}}} as of 9/2008, because the property description of plugins is not planned yet.
-My Idea was to use [[type implementation constraints|StreanTyoeImplConstraint]] for this, which are a special kind of ~ImplType
+My Idea was to use [[type implementation constraints|StreamTypeImplConstraint]] for this, which are a special kind of ~ImplType
 
From 14023d30249e2bf2ec9429eacbb4972a5f9c3284 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 23 Sep 2008 05:09:56 +0200 Subject: [PATCH 08/66] interface brainstorming --- src/proc/control/stypemanager.cpp | 36 +++++++++++++++++++++++++++ src/proc/control/stypemanager.hpp | 27 ++++++++++++++++---- tests/common/streamtypebasicstest.cpp | 10 ++++++++ wiki/renderengine.html | 15 ++++++++--- 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/proc/control/stypemanager.cpp b/src/proc/control/stypemanager.cpp index 9c1e60d08..4f67ac063 100644 --- a/src/proc/control/stypemanager.cpp +++ b/src/proc/control/stypemanager.cpp @@ -21,12 +21,48 @@ * *****************************************************/ +#include "proc/lumiera.hpp" #include "proc/control/stypemanager.hpp" namespace control { + using lumiera::StreamType; + /** */ + StreamType const& + STypeManager::getType (Symbol sTypeID) + { + UNIMPLEMENTED ("get type just by symbolic ID (query defaults manager)"); + } + + + StreamType const& + STypeManager::getType (StreamType::Prototype const& protoType) + { + UNIMPLEMENTED ("build complete StreamType based on prototype; may include querying defaults manager"); + } + + + StreamType const& + STypeManager::getType (StreamType::ImplFacade const& implType) + { + UNIMPLEMENTED ("build complete StreamType round the given implementation type"); + } + + + StreamType::ImplFacade const& + STypeManager::getImpl (Symbol libID, StreamType::Prototype const& protoType) + { + UNIMPLEMENTED ("wire up implementation in compliance to a prototype and using a specific MediaImplLib. really tricky.... "); + } + + + StreamType::ImplFacade const& + STypeManager::getImpl (Symbol libID, StreamType::ImplFacade::TypeTag rawType) + { + UNIMPLEMENTED ("STypeManager basic functionality: wire up implementation facade (impl type) from given raw type of the library"); + } diff --git a/src/proc/control/stypemanager.hpp b/src/proc/control/stypemanager.hpp index 1a5a7ec96..77778a4ef 100644 --- a/src/proc/control/stypemanager.hpp +++ b/src/proc/control/stypemanager.hpp @@ -32,19 +32,36 @@ namespace control { using lumiera::Symbol; + using lumiera::StreamType; class STypeManager { - protected: - virtual ~STypeManager() {}; - public: - lumiera::StreamType const& getType (Symbol sTypeID) ; + /** (re)-access a media stream type using + * just a symbolic ID. Effectively this queries a default */ + StreamType const& getType (Symbol sTypeID) ; + + /** build or retrieve a complete StreamType implementing the given Prototype */ + StreamType const& getType (StreamType::Prototype const& protoType) ; + + /** build or retrieve a complete StreamType incorporating the given implementation type */ + StreamType const& getType (StreamType::ImplFacade const& implType) ; + + /** build or retrieve an implementation (facade) + * utilizing a specific MediaImplLib and implementing the given Prototype. + * @todo find out if this one is really necessary, because it is especially tricky */ + StreamType::ImplFacade const& getImpl (Symbol libID, StreamType::Prototype const& protoType) ; + + /** build or retrieve an implementation (facade) + * wrapping up the actual implementation as designated by the rawType tag, + * which needs to be an implementation type of the mentioned MediaImplLib */ + StreamType::ImplFacade const& getImpl (Symbol libID, StreamType::ImplFacade::TypeTag rawType) ; - ////////////////TODO: Mechanismus erfinden, mit dem sich MediaImplLib-Instanzen einklinken können.... + + ////////////////TODO: design a mechanism allowing to add MediaImplLib implementations by plugin...... }; diff --git a/tests/common/streamtypebasicstest.cpp b/tests/common/streamtypebasicstest.cpp index f8eacf15f..af6d3f764 100644 --- a/tests/common/streamtypebasicstest.cpp +++ b/tests/common/streamtypebasicstest.cpp @@ -46,9 +46,19 @@ namespace lumiera { virtual void run (Arg arg) { buildImplType (); + basicImplTypeProperties (); } void buildImplType () + { + UNIMPLEMENTED ("Access/Factory for the STypeManager??"); + TODO ("set up a GAVL frame type"); + TODO ("use this to retrieve an ImplFacade from the STypeManager"); + UNIMPLEMENTED ("at least preliminary implementation of the MediaImplLib interface for lib GAVL"); + TODO ("how to do a simple consistency check on the returned ImplFacade? can we re-create the GAVL frame type?"); + } + + void basicImplTypeProperties () { StreamType::ImplFacade& iType = createImplType (); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 893bf34ea..ae0094e00 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1058,7 +1058,7 @@ As we don't have a Prolog interpreter on board yet, we utilize a mock store with {{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding a exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one soltution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances. &rarr; [[implementing defaults|DefaultsImplementation]]
-
+
Along the way of working out various [[implementation details|ImplementationDetails]], decisions need to be made on how to understand the different facilities and entities and how to tackle some of the problems. This page is mainly a collection of keywords, summaries and links to further the discussion. And the various decisions should allways be read as proposals to solve some problem at hand...
 
 ''Everything is an object'' &mdash; of course, that's a //no-brainer // todays. Rather, important is what is not "an object", meaning it can't be arranged arbitrarily
@@ -1072,6 +1072,10 @@ We ''separate'' processing (rendering) and configuration (building). We have a [
 An [[EDL]] is just a collection of configured and placed objects (and has no additional, fixed structure). [[Tracks|Track]] form a mere organisational grid, they are grouping devices not first-class entities (a track doesn't "have" a pipe or "is" a video track and the like; it can be configured to behave in such manner by using placements though). [[Pipes|Pipe]] are hooks for making connections and are the only facility to build processing chains. We have global pipes, and each clip is built around a lokal [[source port|ClipSourcePort]] &mdash; and that's all. No special "media viewer" and "arranger", no special role for media sources, no commitment to some fixed media stream types (video and audio). All of this is sort of pushed down to be configuration, represented as asset of some kind. For example, we have [[processing pattern|ProcPatt]] assets to represent the way of building the source network for reading from some media file (including codecs treated like effect plugin nodes)
 
 ''State'' is rigorously ''externalized'' and operations are to be ''scheduled'', to simplify locking and error handling. State is either treated similar to media stream data (as addressable and cacheable data frame), or is represented as "parameter" to be served by some [[parameter provider|ParamProvider]]. Consequently, [[Automation]] is just another kind of parameter, i.e. a function &mdash; how this function is calculated is an encapsulated implementation detail (we don't have "bezier automation", and then maybe a "linear automation", a "mask automation" and yet another way to handle transitions)
+
+Deliberately there is an limitaion on the flexibility of what can be added to the system via Plugins. We allow configuration and parametrisation to be extended and we allow processing and data handling to be extended, but we disallow extensions to the fundamental structure of the system by plugins. They may provide new implementations for already known subsystems, but they can't introduce new subsystems not envisioned in the general design of the application.
+* thus fixed assortments include: the possbile kinds of MObject and [[Asset]], the possible automation parameter data types, the supported kinds of plugin systems
+* while plugins may extend: the supported kinds of media, the [[media handling libraries|MediaImplLib]] used to deal with those media, the session storage backends, the behaviour of placments
 
@@ -1837,7 +1841,7 @@ __5/2008__: the allocation mechanism can surely be improved later, but for now I &rarr; see also LoadingMedia
-
+
The Proc-Layer is designed such as to avoid unnecessary assumptions regarding the properties of the media data and streams. Thus, for anything which is not completely generic, we rely on an abstract [[type description|StreamTypeDescriptor]], which provides a ''Facade'' to an actual library implementation. This way, the fundamental operations can be invoked, like allocating a buffer to hold media data.
 
 In the context of Lumiera and especially in the Proc-Layer, __media implementation library__ means
@@ -1849,7 +1853,8 @@ In the context of Lumiera and especially in the Proc-Layer, __media implementati
 * such a link to an type implementation is registered and maintained by the [[stream type manager|STypeManager]]
 
 !Problem of the implementation data types
-Because we deliberately won't make any asumptions about the implementation library (besides the ones imposed indirectly by the facade interface), we can't integrate the data types of the library first class into the type system. All we can do is to use marker types and rely on the builder to have checked the compatibility of the actual data beforehand.
+Because we deliberately won't make any asumptions about the implementation library (besides the ones imposed indirectly by the facade interface), we can't integrate the data types of the library first class into the type system. All we can do is to use marker types and rely on the builder to have checked the compatibility of the actual data beforehand. 
+It would be possible to circumvent this problem by requiring all supported implementation libraries to be known at compile time, because then the actual media implementation type could be linked to a facade type by generic programming. Indeed, Lumiera follows this route with regards to the possible kinds of MObject or [[Asset]] &mdash; but here to the contraty, being able to include support for a new media data type just by adding a plugin by far outweights the benefits of compile-time checked implementation type selection. So, as a consequence of this design decision we //note the possibility of the media file type discovery code to be misconfigured// and select the //wrong implementation library at runtime.// And thus the render engine needs to be prepared for the source reading node of any pipe to flounder completely, and protect the rest of the system accordingly
 
@@ -3411,7 +3416,7 @@ An implementation constraint can //stand-in// for a completely specified impleme &rarr; see also &raquo;[[Stream Type|StreamType]]&laquo;
-
+
Questions regarding the use of StreamType within the Proc-Layer.
 
 * what is the relation between Buffer and Frame?
@@ -3447,6 +3452,8 @@ To open the file, we need //type discovery code,// resulting in a handle to some
 * an ~ImplFacade
 * a Prototype
 * maybe even from some generic textual ~IDs?
+Together this allows to associate a StreamType to each media source, and thus to derive the Prototype governing the immediately connected [[Pipe]]
+A pipe can by design handle data of one Prototype solely.
 
 !! wiring problems
 When deciding if a connection can be made, we can build up the type information starting out from the source. (this requires some work, but it's //possible,// generally speaking.). Thus, we can allways get an ~ImplType for the "lower end" of the connection, and at least a Prototype for the "output side" &mdash; which should be enough to use the query functions provided by the stream type interfaces

From 83b574bdead734bb5c24bf8b67e7921dc71adf40 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 24 Sep 2008 05:46:26 +0200
Subject: [PATCH 09/66] stream type registry lifecycle

---
 src/proc/control/stypemanager.cpp     | 50 ++++++++++++++++
 src/proc/control/stypemanager.hpp     | 37 ++++++++++++
 src/proc/control/styperegistry.hpp    | 86 +++++++++++++++++++++++++++
 tests/common/streamtypebasicstest.cpp |  8 ++-
 4 files changed, 179 insertions(+), 2 deletions(-)
 create mode 100644 src/proc/control/styperegistry.hpp

diff --git a/src/proc/control/stypemanager.cpp b/src/proc/control/stypemanager.cpp
index 4f67ac063..330d9581e 100644
--- a/src/proc/control/stypemanager.cpp
+++ b/src/proc/control/stypemanager.cpp
@@ -23,11 +23,52 @@
 
 #include "proc/lumiera.hpp"
 #include "proc/control/stypemanager.hpp"
+#include "proc/control/styperegistry.hpp"
+
 
 namespace control {
   
   using lumiera::StreamType;
+  using lumiera::Symbol;
   
+  /* ======= stream type manager lifecycle ==========*/
+  
+  
+  STypeManager::STypeManager()
+    : reg_(0)
+  { 
+    reset();
+  }
+  
+  STypeManager::~STypeManager()
+  { }
+  
+  /** access the system-wide stream type manager instance.
+   *  Implemented as singleton. */
+  lumiera::Singleton STypeManager::instance;
+  
+  
+  void 
+  STypeManager::reset() 
+  {
+    reg_.reset(new Registry);
+    lumiera::Appconfig::lifecycle(ON_STREAMTYPES_RESET);
+  }
+  
+  /** \par 
+   *  LifecycleHook, on which all the basic setup and configuration
+   *  providing the pristine state of the stream type system has to be registered.
+   *  @note plugins providing additional streamtype configuration should register
+   *        their basic setup functions using this hook, which can be done via
+   *        the C interface functions 
+   */
+  Symbol ON_STREAMTYPES_RESET ("ON_STREAMTYPES_RESET");
+
+
+  
+  
+  
+  /* ======= implementation of the public interface ========= */
   
   /** */
   StreamType const&
@@ -67,3 +108,12 @@ namespace control {
   
   
 } // namespace control
+
+
+// ==== C interface for registering setup of basic stream type configuration =======
+
+void 
+lumiera_StreamType_registerInitFunction (void setupFun(void))
+{
+  lumiera::LifecycleHook (control::ON_STREAMTYPES_RESET, setupFun);
+}
diff --git a/src/proc/control/stypemanager.hpp b/src/proc/control/stypemanager.hpp
index 77778a4ef..02edaeb2d 100644
--- a/src/proc/control/stypemanager.hpp
+++ b/src/proc/control/stypemanager.hpp
@@ -26,7 +26,9 @@
 
 
 #include "common/streamtype.hpp"
+#include "common/singleton.hpp"
 
+#include 
 
 
 namespace control {
@@ -38,7 +40,12 @@ namespace control {
   class STypeManager
     {
       
+      class Registry;
+      boost::scoped_ptr reg_;
+      
     public:
+      static lumiera::Singleton instance;
+      
       /** (re)-access a media stream type using
        *  just a symbolic ID. Effectively this queries a default */
       StreamType const& getType (Symbol sTypeID) ;
@@ -62,8 +69,22 @@ namespace control {
       
       
       ////////////////TODO: design a mechanism allowing to add MediaImplLib implementations by plugin......
+    protected:
+      STypeManager();
+      ~STypeManager();
+      
+      friend class lumiera::singleton::StaticCreate;
+      
+      /** Lifecycle: reset all type registration information
+       *  to the generic pristine default state. This includes
+       *  hard wired defaults and defauls provided by type plugins, but
+       *  excludes everything added by the session
+       */
+      void reset() ;
+
     };
   
+  extern Symbol ON_STREAMTYPES_RESET;  ///< triggered to load the generic pristine default  
   
 } // namespace control
 
@@ -74,4 +95,20 @@ namespace proc_interface {
 
 
 } // namespace proc_interface
+
+
+extern "C" { //TODO provide a separate header if some C code or plugin happens to need this...
+  
+  /** any stream type implementation, which needs to be present on the
+   *  pristine default level (without any session specific configuration),
+   *  should register a setup function, which will be called on each
+   *  STypemanager::reset()
+   */
+  void
+  lumiera_StreamType_registerInitFunction (void setupFun(void));
+  
+  // TODO provide a C interface usable from such a setupFun to access the STypeManager registration functions.
+}
+
+
 #endif
diff --git a/src/proc/control/styperegistry.hpp b/src/proc/control/styperegistry.hpp
new file mode 100644
index 000000000..5104a6df8
--- /dev/null
+++ b/src/proc/control/styperegistry.hpp
@@ -0,0 +1,86 @@
+/*
+  STYPEREGISTRY.hpp  -  implementation of the registry for stream type descriptors
+ 
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+ 
+  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 styperegistry.hpp
+ ** This is part of the \i implementation of the stream type manager (include).
+ ** Only used in stypemanager.cpp and accompaning unit tests.
+ ** 
+ ** @see control::STypeManager
+ ** @see lumiera::StreamType
+ **
+ */
+
+
+
+
+#ifndef CONTROL_STYPEREGISTRY_H
+#define CONTROL_STYPEREGISTRY_H
+
+
+//#include "common/query.hpp"
+//#include "common/util.hpp"
+//#include "common/p.hpp"
+
+//#include 
+//#include 
+//#include 
+//#include 
+#include 
+
+
+namespace control {
+
+//  using lumiera::P;
+//  using lumiera::Query;
+  
+//  using std::string;
+//  using boost::format;
+  
+  namespace impl {
+  } // (End) impl namespace
+  
+  
+  
+  /**
+   * @internal Helper for organizing preconfigured default objects.
+   * Maintaines a collection of objects known or encountered as "default"
+   * for a given type. This collection is ordered by "degree of constriction",
+   * which is implemented by counting the number of predicates in the query
+   * used to define or identify each object.
+   * Accessing an object identified by a query causes the query to be resolved
+   * (executed in prolog), which may result in some additional properties of
+   * the object being bound or specified.
+   * @todo as of 3/2008 the real query implementation is missing, and the
+   * exact behaviour has to be defined.
+   */
+  class STypeManager::Registry : private boost::noncopyable
+    {
+    public:
+      
+    };
+  
+  
+  
+} // namespace control
+
+#endif
diff --git a/tests/common/streamtypebasicstest.cpp b/tests/common/streamtypebasicstest.cpp
index af6d3f764..d12dee470 100644
--- a/tests/common/streamtypebasicstest.cpp
+++ b/tests/common/streamtypebasicstest.cpp
@@ -24,6 +24,7 @@
 #include "common/test/run.hpp"
 #include "common/util.hpp"
 
+#include "proc/control/stypemanager.hpp"
 #include "teststreamtypes.hpp"
 
 #include 
@@ -33,7 +34,9 @@ using std::cout;
 
 namespace lumiera {
   namespace test {
-    
+
+    using control::STypeManager;
+  
 
     /*******************************************************************
      * @test check the basic workings of the stream type handling.
@@ -51,7 +54,8 @@ namespace lumiera {
         
         void buildImplType ()
           {
-            UNIMPLEMENTED ("Access/Factory for the STypeManager??");
+            STypeManager& typeManager = STypeManager::instance();
+        
             TODO ("set up a GAVL frame type");
             TODO ("use this to retrieve an ImplFacade from the STypeManager");
             UNIMPLEMENTED ("at least preliminary implementation of the MediaImplLib interface for lib GAVL");

From f80d0a49bd5c81428b2adb0397493477dc6fd70e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 24 Sep 2008 06:36:35 +0200
Subject: [PATCH 10/66] provide for the GAVL impl facade to be registered as
 basic media type. WIP actual implementation missing...

---
 src/lib/external/libgavl.cpp | 21 +++++++++++++++++----
 src/lib/external/libgavl.hpp | 14 +++++++-------
 2 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/src/lib/external/libgavl.cpp b/src/lib/external/libgavl.cpp
index 7b477c871..054feacaf 100644
--- a/src/lib/external/libgavl.cpp
+++ b/src/lib/external/libgavl.cpp
@@ -1,5 +1,5 @@
 /*
-  STypeManager  -  entry point for dealing with media stream types
+  ImplFacadeGAVL  -  facade for integrating the GAVL media handling library
  
   Copyright (C)         Lumiera.org
     2008,               Hermann Vosseler 
@@ -23,6 +23,7 @@
 
 #include "proc/lumiera.hpp"
 #include "lib/external/libgavl.hpp"
+#include "proc/control/stypemanager.hpp"
 
 extern "C" {
 #include 
@@ -31,11 +32,24 @@ extern "C" {
 
 namespace lib {
   namespace external {
-  
-    namespace { // implementation internals
     
+    using control::STypeManager;
+    using control::ON_STREAMTYPES_RESET;
+    using lumiera::LifecycleHook;
+    
+    void
+    provide_GAVL_stream_implementation_types ()
+      {
+        STypeManager& typeManager = STypeManager::instance();
+        UNIMPLEMENTED ("wire up a ImplFacade for GAVL implemented media streams");
+      }
+    
+    namespace { // internal functionality
+    
+        LifecycleHook _register_gavl_types_ (ON_STREAMTYPES_RESET, &provide_GAVL_stream_implementation_types);         
     }
   
+  
   /** 
    * Use an type information struct, which actually has to be 
    * a GAVL frametype (TODO), to wire up an ImplFacade such 
@@ -49,7 +63,6 @@ namespace lib {
     TODO ("any chance to verify that the TypeTag actually points to a GAVL frame type descriptor?");
     UNIMPLEMENTED ("wire up an impl facade with the correct GAVL lib functions for the data type in question");
   }
-
   
   
   
diff --git a/src/lib/external/libgavl.hpp b/src/lib/external/libgavl.hpp
index 26f6b6237..706b08ed2 100644
--- a/src/lib/external/libgavl.hpp
+++ b/src/lib/external/libgavl.hpp
@@ -34,18 +34,20 @@ namespace lib {
   
   
   using lumiera::Symbol;
-    
+  
   using lumiera::StreamType;
   typedef StreamType::ImplFacade ImplFacade;
   typedef StreamType::ImplFacade::TypeTag TypeTag;
-
+  
   
   class LibGavl;
   
   /**
    * Concrete media lib implementation facade
    * allowing to work with GAVL data frames and types
-   * in an implementation agnostic way
+   * in an implementation agnostic way.
+   * @note GAVL types are automagically registered into the
+   * control::STypeManager on reset and thus are always available.
    */
   class ImplFacadeGAVL
     : public ImplFacade
@@ -67,7 +69,7 @@ namespace lib {
       virtual DataBuffer* createFrame ()  const;
     };
   
-    
+  
   class LibGavl
     : public control::MediaImplLib
     {
@@ -78,9 +80,7 @@ namespace lib {
       
       virtual ImplFacadeGAVL const&  getImplFacade (TypeTag*);
     };
-    
-    
-  ////////////////////////////////////TODO: dafür sorgen, daß sich das beim Systemstart in den control::STypeManger einklinkt!!!!!!!!
+  
   
   
   } // namespace external

From 8109badf49c3c415c155f484733541ad3fb964e5 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 24 Sep 2008 06:37:42 +0200
Subject: [PATCH 11/66] outline a test covering the stream type registration
 lifecycle

---
 tests/50components.tests                 |   4 +
 tests/common/streamtypebasicstest.cpp    |  12 +--
 tests/common/streamtypelifecycletest.cpp | 114 +++++++++++++++++++++++
 3 files changed, 124 insertions(+), 6 deletions(-)
 create mode 100644 tests/common/streamtypelifecycletest.cpp

diff --git a/tests/50components.tests b/tests/50components.tests
index 57534f06a..37b867be1 100644
--- a/tests/50components.tests
+++ b/tests/50components.tests
@@ -284,6 +284,10 @@ PLANNED "StreamTypeBasics_test" StreamTypeBasics_test < Testgroup=ALL
diff --git a/tests/common/streamtypebasicstest.cpp b/tests/common/streamtypebasicstest.cpp
index d12dee470..b852e1acd 100644
--- a/tests/common/streamtypebasicstest.cpp
+++ b/tests/common/streamtypebasicstest.cpp
@@ -34,10 +34,10 @@ using std::cout;
 
 namespace lumiera {
   namespace test {
-
+    
     using control::STypeManager;
-  
-
+    
+    
     /*******************************************************************
      * @test check the basic workings of the stream type handling.
      *       create some stream implementation data, build a 
@@ -72,9 +72,9 @@ namespace lumiera {
           }
       };
     
-      LAUNCHER (StreamTypeBasics_test, "unit common");
-
-      
+    LAUNCHER (StreamTypeBasics_test, "unit common");
+    
+    
   } // namespace test
     
 } // namespace lumiera
diff --git a/tests/common/streamtypelifecycletest.cpp b/tests/common/streamtypelifecycletest.cpp
new file mode 100644
index 000000000..82f7cf6b9
--- /dev/null
+++ b/tests/common/streamtypelifecycletest.cpp
@@ -0,0 +1,114 @@
+/*
+  StreamTypeLifecycle(Test)  -  check lifecycle of the stream type registration
+ 
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+ 
+  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 "common/test/run.hpp"
+#include "common/util.hpp"
+
+#include "proc/control/stypemanager.hpp"
+#include "proc/mobject/session.hpp"
+//#include "teststreamtypes.hpp"
+
+#include 
+using std::cout;
+
+
+
+namespace lumiera {
+  namespace test {
+    
+    using mobject::Session;
+    using control::STypeManager;
+    using control::ON_STREAMTYPES_RESET;
+    
+    
+    //////////TODO define a dummy-type-info here
+    //////////TODO
+    
+    void 
+    setup_basicDummyTypeInfo () 
+      { 
+        UNIMPLEMENTED ("setup basic dummy-type-info"); 
+      }
+    
+    namespace // enroll this basic setup to be triggered when the type system is reset 
+      {
+        LifecycleHook _schedule_at_reset (ON_STREAMTYPES_RESET, &setup_basicDummyTypeInfo);         
+      }
+    
+    
+    
+    /*******************************************************************
+     * @test check the stream type registration lifecycle. 
+     *       Any internal or external component (plugin) can extend
+     *       the Proc Layer's registry of media stream types.
+     *       There is a basic pristine set of type information, which is
+     *       restored automatically everytime the STypeManager is reset,
+     *       which in turn happenes before loading a (new) Session.
+     */
+    class StreamTypeLifecycle_test : public Test
+      {
+        virtual void
+        run (Arg arg)
+          {
+            check_pristineState ();
+            register_additional_TypeInfo ();
+            check_pristineState ();
+          }
+        
+        
+        /** @test this test defines a new (dummy) type info
+         *  and schedules it for setop in the pristine state;
+         *  check this info is actually present after resetting
+         *  the stream type manager, while other additional info
+         *  \em not scheduled in this manner is not present 
+         *  in this state
+         */
+        void
+        check_pristineState ()
+          {
+            Session::current.reset();
+            TODO ("verify the test-dummy basic type info is present now...");
+            TODO ("verify the additional type info is *not* present");
+          }
+        
+        /** @test use the stream type manager to register additional
+         *  type info and verify it is used in type resolution.
+         */
+        void
+        register_additional_TypeInfo ()
+          {
+            TODO ("verify the additional type info is *not* present");
+            
+            STypeManager& typeManager = STypeManager::instance();
+            TODO ("use the registration facility to add additional type info");
+            TODO ("verify the additional type info to be present now...");
+          }
+      };
+    
+    LAUNCHER (StreamTypeLifecycle_test, "unit common");
+    
+    
+  } // namespace test
+  
+} // namespace lumiera
+

From 47a416bba372d0b889e2f797565d7cdd8d466a1f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 25 Sep 2008 21:48:43 +0200
Subject: [PATCH 12/66] *Hey* first time to use a GAVL video frame type...

---
 tests/common/streamtypebasicstest.cpp    |  6 ++--
 tests/common/streamtypelifecycletest.cpp |  4 +--
 tests/common/teststreamtypes.hpp         | 43 ++++++++++++++++++++++--
 3 files changed, 46 insertions(+), 7 deletions(-)

diff --git a/tests/common/streamtypebasicstest.cpp b/tests/common/streamtypebasicstest.cpp
index b852e1acd..9a25b4a1d 100644
--- a/tests/common/streamtypebasicstest.cpp
+++ b/tests/common/streamtypebasicstest.cpp
@@ -33,7 +33,7 @@ using std::cout;
 
 
 namespace lumiera {
-  namespace test {
+  namespace test_format {
     
     using control::STypeManager;
     
@@ -56,7 +56,7 @@ namespace lumiera {
           {
             STypeManager& typeManager = STypeManager::instance();
         
-            TODO ("set up a GAVL frame type");
+            gavl_video_format_t rawType = createRawType();
             TODO ("use this to retrieve an ImplFacade from the STypeManager");
             UNIMPLEMENTED ("at least preliminary implementation of the MediaImplLib interface for lib GAVL");
             TODO ("how to do a simple consistency check on the returned ImplFacade? can we re-create the GAVL frame type?");
@@ -75,7 +75,7 @@ namespace lumiera {
     LAUNCHER (StreamTypeBasics_test, "unit common");
     
     
-  } // namespace test
+  } // namespace test_format
     
 } // namespace lumiera
 
diff --git a/tests/common/streamtypelifecycletest.cpp b/tests/common/streamtypelifecycletest.cpp
index 82f7cf6b9..ce260da59 100644
--- a/tests/common/streamtypelifecycletest.cpp
+++ b/tests/common/streamtypelifecycletest.cpp
@@ -34,7 +34,7 @@ using std::cout;
 
 
 namespace lumiera {
-  namespace test {
+  namespace test_format {
     
     using mobject::Session;
     using control::STypeManager;
@@ -108,7 +108,7 @@ namespace lumiera {
     LAUNCHER (StreamTypeLifecycle_test, "unit common");
     
     
-  } // namespace test
+  } // namespace test_format
   
 } // namespace lumiera
 
diff --git a/tests/common/teststreamtypes.hpp b/tests/common/teststreamtypes.hpp
index 03409c7a9..e0d53f9e2 100644
--- a/tests/common/teststreamtypes.hpp
+++ b/tests/common/teststreamtypes.hpp
@@ -30,10 +30,49 @@
 #include "common/streamtype.hpp"
 #include "proc/control/stypemanager.hpp"
 
+extern "C" {
+#include 
+}
 
 
 namespace lumiera {
-  namespace test {
+  namespace test_format {
+  
+    namespace { // constants used to parametrize tests
+    
+      const int TEST_IMG_WIDTH = 40;
+      const int TEST_IMG_HEIGHT = 30;
+      
+      const int TEST_FRAME_DUR = GAVL_TIME_SCALE / 25; 
+    }
+    
+    /** Helper: create an raw GAVL type descriptor
+     *  usable for generating a Lumiera StreamType 
+     */
+    inline gavl_video_format_t
+    createRawType ()
+    {
+      gavl_video_format_t type;
+      
+      type.pixelformat = GAVL_RGB_24;
+      type.interlace_mode = GAVL_INTERLACE_NONE;
+      type.framerate_mode = GAVL_FRAMERATE_CONSTANT;
+      type.chroma_placement = GAVL_CHROMA_PLACEMENT_DEFAULT;
+      
+      type.image_width  = TEST_IMG_WIDTH;   // Width of the image in pixels
+      type.image_height = TEST_IMG_WIDTH;   // Height of the image in pixels
+      type.frame_width  = TEST_IMG_WIDTH;   // Width of the frame buffer in pixels, might be larger than image_width 
+      type.frame_height = TEST_IMG_WIDTH;   // Height of the frame buffer in pixels, might be larger than image_height
+  
+      type.pixel_width  = 1;              // Relative width of a pixel (pixel aspect ratio is pixel_width/pixel_height)
+      type.pixel_height = 1;             // Relative height of a pixel (pixel aspect ratio is pixel_width/pixel_height)
+
+      type.frame_duration = TEST_FRAME_DUR; // Duration of a frame in timescale tics. 
+      type.timescale = GAVL_TIME_SCALE;     // Timescale in tics per second  (is defined to be 1000000 as of 9/2008)
+      
+      return type;
+    }
+    
     
     /** Helper: create an implementation frame
      *  and build the corresponding streamtype
@@ -46,7 +85,7 @@ namespace lumiera {
     
     
     
-  } // namespace test
+  } // namespace test_format
 
 } // namespace lumiera
 #endif

From feb64fac016d54d32909950f6e1ee4a034fb341f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 26 Sep 2008 04:57:20 +0200
Subject: [PATCH 13/66] outline: use of the raw type info for fetching a
 ImplFacade

---
 src/common/streamtype.hpp             | 32 ++++++++++++++++++++++-----
 src/lib/external/libgavl.cpp          |  2 +-
 src/lib/external/libgavl.hpp          |  2 +-
 src/proc/control/mediaimpllib.hpp     |  2 +-
 src/proc/control/stypemanager.cpp     | 12 +++++-----
 src/proc/control/stypemanager.hpp     | 26 +++++++++++++++++-----
 tests/common/streamtypebasicstest.cpp | 10 ++++++---
 tests/common/teststreamtypes.hpp      | 17 +++++++++-----
 wiki/renderengine.html                |  9 ++++----
 9 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp
index 6319c40a5..dfebfca95 100644
--- a/src/common/streamtype.hpp
+++ b/src/common/streamtype.hpp
@@ -102,8 +102,7 @@ namespace lumiera {
     public:
       Symbol libraryID;
       
-      /** placeholder definition for implementation specific type information */
-      struct TypeTag { };
+      class TypeTag ; 
       
       /** placeholder definition for the contents of a data buffer */
       struct DataBuffer { };
@@ -125,14 +124,15 @@ namespace lumiera {
   
   
   /**
-   * 
+   * Special case of an implementation type being only partialiy specified
+   * Besides requiring some aspect of the implementation type, there is the
+   * promise to fill in defaults to build a complete implementation type
+   * if necessary.
    */
   class StreamType::ImplConstraint
     : public StreamType::ImplFacade
     {
     public:
-      Symbol libraryID;
-      
       virtual bool canConvert (ImplFacade const& other)  const =0;
       virtual bool canConvert (StreamType const& other)  const =0;
       
@@ -153,6 +153,28 @@ namespace lumiera {
       //TODO: do we need functions to represent and describe this constraint?
       
     };
+  
+  
+    /** 
+     * opaque placeholder (type erasure) 
+     * for implementation specific type info.
+     * Intended to be passed to a concrete
+     * MediaImplLib to build an ImplFacade. 
+     */
+  class StreamType::ImplFacade::TypeTag
+    {
+      void* rawTypeStruct_;
+      
+    public:
+      Symbol libraryID;
+      
+      template
+      TypeTag (Symbol lID, TY& rawType)
+        : rawTypeStruct_(&rawType),
+          libraryID(lID)
+        { }
+    };
+
     
   
 
diff --git a/src/lib/external/libgavl.cpp b/src/lib/external/libgavl.cpp
index 054feacaf..740515052 100644
--- a/src/lib/external/libgavl.cpp
+++ b/src/lib/external/libgavl.cpp
@@ -58,7 +58,7 @@ namespace lib {
    * @todo how to distinguish the audio and the video case?
    */
   ImplFacadeGAVL const&
-  LibGavl::getImplFacade (TypeTag*)
+  LibGavl::getImplFacade (TypeTag&)
   {
     TODO ("any chance to verify that the TypeTag actually points to a GAVL frame type descriptor?");
     UNIMPLEMENTED ("wire up an impl facade with the correct GAVL lib functions for the data type in question");
diff --git a/src/lib/external/libgavl.hpp b/src/lib/external/libgavl.hpp
index 706b08ed2..945aec2b7 100644
--- a/src/lib/external/libgavl.hpp
+++ b/src/lib/external/libgavl.hpp
@@ -78,7 +78,7 @@ namespace lib {
     public:
       virtual Symbol getLibID()  const  { return "GAVL"; }
       
-      virtual ImplFacadeGAVL const&  getImplFacade (TypeTag*);
+      virtual ImplFacadeGAVL const&  getImplFacade (TypeTag&);
     };
   
   
diff --git a/src/proc/control/mediaimpllib.hpp b/src/proc/control/mediaimpllib.hpp
index c150ad913..396d573ef 100644
--- a/src/proc/control/mediaimpllib.hpp
+++ b/src/proc/control/mediaimpllib.hpp
@@ -46,7 +46,7 @@ namespace control {
     public:
       virtual Symbol getLibID()  const =0;
       
-      virtual ImplFacade const&  getImplFacade (TypeTag*) =0;
+      virtual ImplFacade const&  getImplFacade (TypeTag&) =0;
     };
   
   
diff --git a/src/proc/control/stypemanager.cpp b/src/proc/control/stypemanager.cpp
index 330d9581e..d28ee3eb0 100644
--- a/src/proc/control/stypemanager.cpp
+++ b/src/proc/control/stypemanager.cpp
@@ -36,7 +36,7 @@ namespace control {
   
   STypeManager::STypeManager()
     : reg_(0)
-  { 
+  {
     reset();
   }
   
@@ -55,7 +55,7 @@ namespace control {
     lumiera::Appconfig::lifecycle(ON_STREAMTYPES_RESET);
   }
   
-  /** \par 
+  /** \par
    *  LifecycleHook, on which all the basic setup and configuration
    *  providing the pristine state of the stream type system has to be registered.
    *  @note plugins providing additional streamtype configuration should register
@@ -63,8 +63,8 @@ namespace control {
    *        the C interface functions 
    */
   Symbol ON_STREAMTYPES_RESET ("ON_STREAMTYPES_RESET");
-
-
+  
+  
   
   
   
@@ -100,13 +100,11 @@ namespace control {
   
   
   StreamType::ImplFacade const&
-  STypeManager::getImpl (Symbol libID, StreamType::ImplFacade::TypeTag rawType)
+  STypeManager::fetchImpl (StreamType::ImplFacade::TypeTag rawType)
   {
     UNIMPLEMENTED ("STypeManager basic functionality: wire up implementation facade (impl type) from given raw type of the library");
   }
   
-  
-  
 } // namespace control
 
 
diff --git a/src/proc/control/stypemanager.hpp b/src/proc/control/stypemanager.hpp
index 02edaeb2d..48445a427 100644
--- a/src/proc/control/stypemanager.hpp
+++ b/src/proc/control/stypemanager.hpp
@@ -46,6 +46,8 @@ namespace control {
     public:
       static lumiera::Singleton instance;
       
+      typedef StreamType::ImplFacade ImplFacade;
+      
       /** (re)-access a media stream type using
        *  just a symbolic ID. Effectively this queries a default */
       StreamType const& getType (Symbol sTypeID) ;
@@ -55,22 +57,23 @@ namespace control {
       
       /** build or retrieve a complete StreamType incorporating the given implementation type */
       StreamType const& getType (StreamType::ImplFacade const& implType) ;
-
+      
       /** build or retrieve an implementation (facade)
        *  utilizing a specific MediaImplLib and implementing the given Prototype.
        *  @todo find out if this one is really necessary, because it is especially tricky */
-      StreamType::ImplFacade const& getImpl (Symbol libID, StreamType::Prototype const& protoType) ;
+      ImplFacade const& getImpl (Symbol libID, StreamType::Prototype const& protoType) ;
       
       /** build or retrieve an implementation (facade)
        *  wrapping up the actual implementation as designated by the rawType tag,
        *  which needs to be an implementation type of the mentioned MediaImplLib */
-      StreamType::ImplFacade const& getImpl (Symbol libID, StreamType::ImplFacade::TypeTag rawType) ;
+      template
+      ImplFacade const& getImpl (Symbol libID, TY& rawType) ;
       
       
       
       ////////////////TODO: design a mechanism allowing to add MediaImplLib implementations by plugin......
     protected:
-      STypeManager();
+      STypeManager() ;
       ~STypeManager();
       
       friend class lumiera::singleton::StaticCreate;
@@ -81,10 +84,21 @@ namespace control {
        *  excludes everything added by the session
        */
       void reset() ;
-
+      
+    private:
+      ImplFacade const& fetchImpl (StreamType::ImplFacade::TypeTag);
     };
   
-  extern Symbol ON_STREAMTYPES_RESET;  ///< triggered to load the generic pristine default  
+  extern Symbol ON_STREAMTYPES_RESET;  ///< triggered to load the generic pristine default
+  
+  
+  template
+  StreamType::ImplFacade const&
+  STypeManager::getImpl (Symbol libID, TY& rawType)
+  {
+    return fetchImpl (ImplFacade::TypeTag (libID,rawType));
+  }
+  
   
 } // namespace control
 
diff --git a/tests/common/streamtypebasicstest.cpp b/tests/common/streamtypebasicstest.cpp
index 9a25b4a1d..6097da0a7 100644
--- a/tests/common/streamtypebasicstest.cpp
+++ b/tests/common/streamtypebasicstest.cpp
@@ -36,6 +36,7 @@ namespace lumiera {
   namespace test_format {
     
     using control::STypeManager;
+    typedef StreamType::ImplFacade const& ImplType;
     
     
     /*******************************************************************
@@ -56,15 +57,18 @@ namespace lumiera {
           {
             STypeManager& typeManager = STypeManager::instance();
         
-            gavl_video_format_t rawType = createRawType();
-            TODO ("use this to retrieve an ImplFacade from the STypeManager");
+            gavl_video_format_t rawType = test_createRawType();
+            ImplType iTy (typeManager.getImpl (GAVL, rawType));
+            
             UNIMPLEMENTED ("at least preliminary implementation of the MediaImplLib interface for lib GAVL");
+            
             TODO ("how to do a simple consistency check on the returned ImplFacade? can we re-create the GAVL frame type?");
+            ASSERT (GAVL==iTy.libraryID);
           }
         
         void basicImplTypeProperties ()
           {
-            StreamType::ImplFacade& iType = createImplType ();
+            ImplType iTy = test_createImplType ();
             
             UNIMPLEMENTED ("get a lib descriptor"); 
             UNIMPLEMENTED ("check the lib of the type"); 
diff --git a/tests/common/teststreamtypes.hpp b/tests/common/teststreamtypes.hpp
index e0d53f9e2..3505b7580 100644
--- a/tests/common/teststreamtypes.hpp
+++ b/tests/common/teststreamtypes.hpp
@@ -45,12 +45,16 @@ namespace lumiera {
       
       const int TEST_FRAME_DUR = GAVL_TIME_SCALE / 25; 
     }
+        
+    Symbol GAVL = "GAVL";
+    
+    
     
     /** Helper: create an raw GAVL type descriptor
      *  usable for generating a Lumiera StreamType 
      */
     inline gavl_video_format_t
-    createRawType ()
+    test_createRawType ()
     {
       gavl_video_format_t type;
       
@@ -63,10 +67,10 @@ namespace lumiera {
       type.image_height = TEST_IMG_WIDTH;   // Height of the image in pixels
       type.frame_width  = TEST_IMG_WIDTH;   // Width of the frame buffer in pixels, might be larger than image_width 
       type.frame_height = TEST_IMG_WIDTH;   // Height of the frame buffer in pixels, might be larger than image_height
-  
+      
       type.pixel_width  = 1;              // Relative width of a pixel (pixel aspect ratio is pixel_width/pixel_height)
       type.pixel_height = 1;             // Relative height of a pixel (pixel aspect ratio is pixel_width/pixel_height)
-
+      
       type.frame_duration = TEST_FRAME_DUR; // Duration of a frame in timescale tics. 
       type.timescale = GAVL_TIME_SCALE;     // Timescale in tics per second  (is defined to be 1000000 as of 9/2008)
       
@@ -77,10 +81,11 @@ namespace lumiera {
     /** Helper: create an implementation frame
      *  and build the corresponding streamtype
      */
-    inline StreamType::ImplFacade&
-    createImplType ()
+    inline StreamType::ImplFacade const&
+    test_createImplType ()
     {
-      UNIMPLEMENTED ("create a test stream type from a given GAVL type tag");
+      gavl_video_format_t rawType = test_createRawType();
+      return control::STypeManager::instance().getImpl (GAVL, rawType);
     }
     
     
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index ae0094e00..c1681b9f6 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1841,7 +1841,7 @@ __5/2008__: the allocation mechanism can surely be improved later, but for now I
 &rarr; see also LoadingMedia
 
-
+
The Proc-Layer is designed such as to avoid unnecessary assumptions regarding the properties of the media data and streams. Thus, for anything which is not completely generic, we rely on an abstract [[type description|StreamTypeDescriptor]], which provides a ''Facade'' to an actual library implementation. This way, the fundamental operations can be invoked, like allocating a buffer to hold media data.
 
 In the context of Lumiera and especially in the Proc-Layer, __media implementation library__ means
@@ -1849,12 +1849,12 @@ In the context of Lumiera and especially in the Proc-Layer, __media implementati
 * such as to provide the minimal set of operations
 ** allocating a frame buffer
 ** describing the type of the data within such a buffer
-* and this subsystem or external library has been integrated to be used by Lumiera by writing adaptation code for accessing these basic operations through the [[implementation facade interface|StreamTypeImplFacade]]
+* and this subsystem or external library has been integrated to be used through Lumiera by writing adaptation code for accessing these basic operations through the [[implementation facade interface|StreamTypeImplFacade]]
 * such a link to an type implementation is registered and maintained by the [[stream type manager|STypeManager]]
 
 !Problem of the implementation data types
 Because we deliberately won't make any asumptions about the implementation library (besides the ones imposed indirectly by the facade interface), we can't integrate the data types of the library first class into the type system. All we can do is to use marker types and rely on the builder to have checked the compatibility of the actual data beforehand. 
-It would be possible to circumvent this problem by requiring all supported implementation libraries to be known at compile time, because then the actual media implementation type could be linked to a facade type by generic programming. Indeed, Lumiera follows this route with regards to the possible kinds of MObject or [[Asset]] &mdash; but here to the contraty, being able to include support for a new media data type just by adding a plugin by far outweights the benefits of compile-time checked implementation type selection. So, as a consequence of this design decision we //note the possibility of the media file type discovery code to be misconfigured// and select the //wrong implementation library at runtime.// And thus the render engine needs to be prepared for the source reading node of any pipe to flounder completely, and protect the rest of the system accordingly
+It would be possible to circumvent this problem by requiring all supported implementation libraries to be known at compile time, because then the actual media implementation type could be linked to a facade type by generic programming. Indeed, Lumiera follows this route with regards to the possible kinds of MObject or [[Asset]] &mdash; but to the contraty, for the problem in question here, being able to include support for a new media data type just by adding a plugin by far outweights the benefits of compile-time checked implementation type selection. So, as a consequence of this design decision we //note the possibility of the media file type discovery code to be misconfigured// and select the //wrong implementation library at runtime.// And thus the render engine needs to be prepared for the source reading node of any pipe to flounder completely, and protect the rest of the system accordingly
 
@@ -3406,7 +3406,7 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu An implementation constraint can //stand-in// for a completely specified implementation type (meaning it's a sub interface of the latter). But actually using it in this way may cause a call to the [[defaults manager|DefaultsImplementation]] to fill in any missing information. An example would be to call {{{createFrame()}}} on the type constraint object, which means being able to allocate memory to hold a data frame, with properties in compliance with the given type constraint. Of cousre, then we need to know all the properties of this stream type, which is where the defaults manager is queried. This allows session customisation to kick in, but may fail under certain cicumstances.
-
+
Common interface for dealing with the implementation of media stream data. From a high level perspective, the various kinds of media ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}) exhibit similar behaviour, while on the implementation level not even the common classification can be settled down to a complete general and useful scheme. Thus, we need separate library implementations for deailing with the various sorts of media data, all providing at least a set of basic operations:
 * set up a buffer
 * create or accept a frame
@@ -3414,6 +3414,7 @@ An implementation constraint can //stand-in// for a completely specified impleme
 * ...?
 
 &rarr; see also &raquo;[[Stream Type|StreamType]]&laquo;
+//Note:// there is a sort-of "degraded" variant just requiring some &rarr; [[implementation constraint|StreamTypeImplConstraint]] to hold
 
From 873910f0b8162a207267b2eebaf96086f8f1a35c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 28 Sep 2008 04:05:10 +0200 Subject: [PATCH 14/66] WIP considerations about querying --- src/common/streamtype.hpp | 18 +++---- src/lib/external/libgavl.hpp | 1 + src/proc/control/stypemanager.hpp | 1 + src/tool/try.cpp | 80 ------------------------------- wiki/renderengine.html | 25 +++++++--- 5 files changed, 30 insertions(+), 95 deletions(-) diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp index dfebfca95..28c87bfa4 100644 --- a/src/common/streamtype.hpp +++ b/src/common/streamtype.hpp @@ -70,7 +70,6 @@ namespace lumiera { class ImplConstraint; - MediaKind kind; Prototype const& prototype; ImplFacade * implType; Usage intentionTag; @@ -85,13 +84,14 @@ namespace lumiera { struct StreamType::Prototype { Symbol id; + MediaKind kind; bool subsumes (Prototype const& other) const; - bool canConvert (Prototype const& other) const; + bool canRender (Prototype const& other) const; }; - + /** * A (more or less) concrete implementation type, wired up @@ -106,7 +106,7 @@ namespace lumiera { /** placeholder definition for the contents of a data buffer */ struct DataBuffer { }; - + virtual bool operator== (ImplFacade const& other) const =0; virtual bool operator== (StreamType const& other) const =0; @@ -115,6 +115,7 @@ namespace lumiera { virtual bool canConvert (StreamType const& other) const =0; virtual DataBuffer* createFrame () const =0; + virtual MediaKind getKind() const =0; virtual ~ImplFacade() {}; @@ -144,7 +145,7 @@ namespace lumiera { /** create a default impl type in accordance to this constraint * and use it to create a new framebuffer */ virtual DataBuffer* createFrame () const =0; - + /** similarily create a impl type which complies to this constraint * as well as to the additional constraints (e.g. frame size). * Create a new framebuffer of the resutling type */ @@ -174,10 +175,9 @@ namespace lumiera { libraryID(lID) { } }; - - - - + + + } // namespace lumiera #endif diff --git a/src/lib/external/libgavl.hpp b/src/lib/external/libgavl.hpp index 945aec2b7..d8d7bc412 100644 --- a/src/lib/external/libgavl.hpp +++ b/src/lib/external/libgavl.hpp @@ -66,6 +66,7 @@ namespace lib { virtual bool canConvert (ImplFacade const& other) const; virtual bool canConvert (StreamType const& other) const; + virtual StreamType::MediaKind getKind() const; virtual DataBuffer* createFrame () const; }; diff --git a/src/proc/control/stypemanager.hpp b/src/proc/control/stypemanager.hpp index 48445a427..dcefe74f3 100644 --- a/src/proc/control/stypemanager.hpp +++ b/src/proc/control/stypemanager.hpp @@ -48,6 +48,7 @@ namespace control { typedef StreamType::ImplFacade ImplFacade; + /** (re)-access a media stream type using * just a symbolic ID. Effectively this queries a default */ StreamType const& getType (Symbol sTypeID) ; diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 3f51bcaf6..167946552 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -20,101 +20,21 @@ using std::string; using std::cout; -using std::ostream; using boost::format; -#include -using boost::enable_if; -#include -using boost::is_base_of; -#include -#include - - -#include "common/meta/generator.hpp" -using lumiera::typelist::NullType; -using lumiera::typelist::Node; -using lumiera::typelist::Types; - - namespace { boost::format fmt ("<%2i>"); - /** constant-wrapper type for debugging purposes, - * usable for generating lists of distinghishable types - */ - template - struct Num - { - enum{ VAL=I }; - static string str () { return boost::str (fmt % I); } - Num() - { - cout << Num::str(); - } - }; - - - - template class _CandidateTemplate_> - class Instantiation - { - template - struct If_possibleArgument : _CandidateTemplate_ - { - typedef void Type; - }; - - public: - - template - struct Test - : boost::false_type {}; - - template - struct Test::Type > - : boost::true_type {}; - - }; - } - struct Boing { typedef boost::true_type is_defined; }; - - template struct Zoing ; - - template<> struct Zoing<2> : Boing { enum{wau = 2}; }; - template<> struct Zoing<5> : Boing { enum{wau = 5}; }; - - typedef char yes_type; - struct no_type - { - char padding[8]; - }; - - template - yes_type check(typename U::is_defined *); - template - no_type check(...); - - int main (int argc, char* argv[]) { NOBUG_INIT; - typedef Zoing<2> Z2; - typedef Zoing<3> Z3; - typedef Zoing<5> Z5; - - cout << sizeof(check(0)) << " / " - << sizeof(check(0)) << " / " - << sizeof(check(0)) ; - - cout << "\ngulp\n"; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index c1681b9f6..d3b2b7c47 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3354,7 +3354,7 @@ Consequently, as we can't get away with an fixed Enum of all stream prototypes, NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus linear PCM...
-
+
//how to classify and describe media streams//
 Media data is understood to appear structured as stream(s) over time. While there may be an inherent internal structuring, at a given perspective ''any stream is a unit and homogeneous''. In the context of digital media data processing, streams are always ''quantized'', which means they appear as a temporal sequence of data chunks called ''frames''.
 
@@ -3376,8 +3376,8 @@ A stream type is denoted by a StreamTypeID, which is an identifier, acting as an
 !! Classification
 Within the Proc-Layer, media streams are treated largely in a similar manner. But, looking closer, note everything can be connected together, while on the other hand there may be some classes of media streams which can be considered //equivalent// in most respects. Thus, it seems reasonable to separate the distinction between various media streams into several levels
 * Each media belongs to a fundamental ''kind'' of media, examples being __Video__, __Image__, __Audio__, __MIDI__,... Media streams of different kind can be considered somewhat "completely separate" &mdash; just the handling of each of those media kinds follows a common //generic pattern// augmented with specialisations. Basically, it is //impossible to connect// media streams of different kind. Under some circumstances there may be the possibility of a //transformation// though. For example, a still image can be incorporated into video, sound may be visualized, MIDI may control a sound synthesizer.
-* Below the level of distinct kinds of media streams, within every kind we have an open ended collection of ''prototypes'', which, when compared directly may each be quite distinct and different, but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video lacking depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating sound systems (5.1, 7.1,...), we have common stereophonic and monaural audio. It is considered important to retain some openness and configurability within this level of distinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable for some production to distinguish between digitized film and video NTSC and PAL, while in another production everything is just "video" and can be converted automatically. The most noticeable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype. (&rarr; [[more|StreamPrototype]])
-* Besides the distinction by prototypes, there are the various media ''implementation types''. This classification is not necessarily hierarchically related to the prototype classification, while in practice commonly there will be some sort of dependency. For example, both stereophonic and monaural audio may be implemented as 96kHz 24bit PCM with just a different number of channel streams, as well we may have a dedicated stereo audio stream with two channels multiplexed into a single stream. For dealing with media streams of various implementation type, we need //library// routines, which also yield a //type classification system.// Most notably, for raw sound and video data we use the GAVL library, which defines a classification system for buffers and streams.
+* Below the level of distinct kinds of media streams, within every kind we have an open ended collection of ''prototypes'', which, when compared directly, may each be quite distinct and different, but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video lacking depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating sound systems (5.1, 7.1,...), we have common stereophonic and monaural audio. It is considered important to retain some openness and configurability within this level of distinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable for some production to distinguish between digitized film and video NTSC and PAL, while in another production everything is just "video" and can be converted automatically. The most noticeable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype. (&rarr; [[more|StreamPrototype]])
+* Besides the distinction by prototypes, there are the various media ''implementation types''. This classification is not necessarily hierarchically related to the prototype classification, while in practice commonly there will be some sort of dependency. For example, both stereophonic and monaural audio may be implemented as 96kHz 24bit PCM with just a different number of channel streams, as well we may have a dedicated stereo audio stream with two channels multiplexed into a single stream. For dealing with media streams of various implementation type, we need //library// routines, which also yield a //type classification system.// Most notably, for raw sound and video data we use the [[GAVL]] library, which defines a classification system for buffers and streams.
 * Besides the type classification detailed thus far, we introduce an ''intention tag''. This is a synthetic classification owned by Lumiera and used for internal wiring decisions. Currently (8/08), we recognize the following intention tags: __Source__, __Raw__, __Intermediary__ and __Target__. Only media streams tagged as __Raw__ can be processed.
 
 !! Media handling requirements involving stream type classification
@@ -3385,7 +3385,9 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu
 * determine if a given media data source and sink can be connected, and how.
 * determine and enumerate the internal structure of a stream.
 * discover processing facilities
-&rarr; see StreamTypeUse
+&rarr; see StreamTypeUse +&rarr; [[querying types|StreamTypeQuery]] +
A description and classification record usable to find out about the properties of a media stream. The stream type descriptor can be accessed using an unique StreamTypeID. The information contained in this descriptor record can intentionally be //incomplete,// in which case the descriptor captures a class of matching media stream types. The following information is maintained:
@@ -3417,13 +3419,24 @@ An implementation constraint can //stand-in// for a completely specified impleme
 //Note:// there is a sort-of "degraded" variant just requiring some &rarr; [[implementation constraint|StreamTypeImplConstraint]] to hold
 
-
+
+
Querying for media stream type information comes in various flavours
+* you may find a structural object (pipe, output, processing patten) associated with / able to deal with a certain stream type
+* you may need a StreamTypeDescriptor for an existing stream given as implementation data
+* you may want to build or complete type information from partial specification.
+Mostly, those queries involve the ConfigRules system in some way or the other. The [[prototype-|StreamPrototype]] and [[implementation type|StreamTypeImplFacade]]-interfaces themselves are mostly a facade for issuing appropriate queries. Some objects (especially [[pipes|Pipe]]) are tied to a certain stream type and thus store a direct link to type information. Others are just associated with a type by virtue of the DefaultsManagement.
+
+
+
Questions regarding the use of StreamType within the Proc-Layer.
-
 * what is the relation between Buffer and Frame?
 * how to get the required size of a Buffer?
 * who does buffer allocations and how?
 
+Mostly, stream types are used for querying, either to decide if they can be connected, or to find usable processing modules.
+Even building a stream type from partial information involves some sort of query.
+&rarr; more on [[media stream type queries|StreamTypeQuery]]
+
 !creating stream types
 seemingly stream types are created based on an already existing media stream (or a Frame of media data?). {{red{really?}}}
 The other use case seems to be that of an //incomplete// stream type based on a [[Prototype|StreamPrototype]]

From 064bf703934441dcf881d77b2897f781543ce5b5 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 28 Sep 2008 05:23:32 +0200
Subject: [PATCH 15/66] WIP analysis of the various query situations

---
 wiki/renderengine.html | 36 +++++++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index d3b2b7c47..83bb69dc3 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -3419,15 +3419,43 @@ An implementation constraint can //stand-in// for a completely specified impleme
 //Note:// there is a sort-of "degraded" variant just requiring some &rarr; [[implementation constraint|StreamTypeImplConstraint]] to hold
 
-
+
Querying for media stream type information comes in various flavours
 * you may find a structural object (pipe, output, processing patten) associated with / able to deal with a certain stream type
 * you may need a StreamTypeDescriptor for an existing stream given as implementation data
 * you may want to build or complete type information from partial specification.
 Mostly, those queries involve the ConfigRules system in some way or the other. The [[prototype-|StreamPrototype]] and [[implementation type|StreamTypeImplFacade]]-interfaces themselves are mostly a facade for issuing appropriate queries. Some objects (especially [[pipes|Pipe]]) are tied to a certain stream type and thus store a direct link to type information. Others are just associated with a type by virtue of the DefaultsManagement.
+
+The //problem// with this pivotal role of the config rules is that &mdash; from a design perspective &mdash; not much can be said specifically, besides //"you may be able to find out...", "...depends on the defaults and the session configuration".// This way, a good deal of crucial behaviour is pushed out of the core implementation (and it's quite intentionally being done this way). What can be done regarding the design of the core is mostly to setup a framework for the rules and determine possible ''query situations''.
+
+!the kind of media
+the information of the fundamental media kind (video, audio, text, MIDI,...) is assiciated with the prototype, for technical reasons. Prototype information is mandatory for each StreamType, and the impl facade provides a query function (because some implementation libraries, e.g. [[GAVL]], support multiple kinds of media).
+
+!query for a prototype
+__Situation__: given an implementation type, find a prototype to subsume it.
+Required only for building a complete ~StreamType which isn't known at this point.
+The general case of this query is //quite hairy,// because the solution is not necessary clear and unique. And, worse still, it is related to the semantics, requiring semantic information and tagging to be maintained somewhere. For example, while the computer can't "know" what stereopohinc audio is (only a human can by listening to a stereophoic playback and deciding if it actually does convey a spatical sound image), in most cases we can overcome this problem by using the //heuristical rule// of assuming the prototype "stereophonic" when given two identically typed audio channels. This example also shows the necessity of ordering heuristic rules to be able to pick a best fit.
+
+We can inject two different kinds of fallback solutions for this kind of query:
+* we can always build a "catch-all" prototype just based on the kind of media (e.g. {{{prototype(video).}}}). This should match with lowest priority
+* we can search for existing ~StreamTypes with the same impl type, or an impl type which is //equivalent convertible// (see &rarr; StreamConversion). 
+The latter case can yield multiple solutions, which isn't any problem, because the match is limited to classes of equivalent stream implementation, which would be subsumed under the same prototype anyway. Even if the registry holds different prototypes linked to the same implementation type, they would be convertible and thus could stand in for one another. Together this results in the implementation
+# try to get a direct match to an existing impl type which has an associated (complete) ~StreamType, thus bypassing the ConfigRules system altogether
+# run a {{{Query<Prototype>}}} for the given implementation type
+# do the search within equivalence class as described above
+# fall back to the media kind.
+{{red{TODO: how to deal with the problem of hijacking a prototype?}}} &rarr; see [[here|StreamTypeUse]]
+
+!query for an implementation
+__Situation 1__: given an partially specified ~StreamType (just an [[constraint|StreamTypeImplConstraint]])
+__Situation 2__: find an implementation for a given prototype (without any further impl type guidlines)
+Both cases have to go though the [[defaults manager|DefaultsManagement]] in some way, in order to give any default configuration a chance to kick in. This is //one of the most important use cases// of the defaults system: the ability to configure a default fromat for all streams with certain semantic classification. {{{prototype(video)}}} by default is RGBA 24bit non-interlaced for example.
+But after having queried the defaults system, there remains the problem to build a new solution (which will then automatically become a default
+
+!query for an (complete) StreamType
 
-
+
Questions regarding the use of StreamType within the Proc-Layer.
 * what is the relation between Buffer and Frame?
 * how to get the required size of a Buffer?
@@ -3443,7 +3471,9 @@ The other use case seems to be that of an //incomplete// stream type based on a
 
 !Prototype
 According to my current understanding, a prototype is merely a classification entity. But then &mdash; how to bootstrap a Prototype?
-And how to do the classification of an existing implementation type
+And how to do the classification of an existing implementation type.
+
+Besides, there is the problem of //hijacking a prototype:// when a specific implementation type gets tied to a rather generic protoype, like {{{protoype(video)}}}, how to comply to the rule of prototypes subsuming a class of equivalent implementations?
 
 !Defaults and partial specification
 A StreamType need not be completely defined. It is sufficient to specify the media kind and the Prototype. The implementation type may be just given as a constraint, thus defining some properties and leaving out others. When creating a frame buffer based upon such an //incomplete type,// [[defaults|DefaultsManagement]] are queried to fill in the missing parts.

From e541c71995355213768f9bf4bd8574a4f42fcb8f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 2 Oct 2008 05:40:10 +0200
Subject: [PATCH 16/66] WIP continued the analysis of queries / conversions

---
 wiki/renderengine.html | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 83bb69dc3..b115d0ce6 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -3338,6 +3338,13 @@ if (oldText.indexOf("SplashScreen")==-1)
 * in a future version, it may also encapsulate the communication in a distributed render farm
 
+
+
Conversion of a media stream into a stream of another type is done by a processor module (plugin). The problem of finding such a module is closely related to the StreamType and especially [[problems of querying|StreamTypeQuery]] for such. There can be different kinds of conversions, and the existance or non-existance of such an conversion can influence the stream type classification.
+* different //kinds of media// can be ''transformed'' into each other
+* stream types //subsumed// by a given prototype should be ''lossless convertible''
+* besides, between different stream //implementation types,// there can be a ''rendering'' (lossy conversion) &mdash; or no conversion at all.
+
+
The stream Prototype is part of the specification of a media stream's type. It is a semantic (or problem domain oriented) concept and should be distinguished from the actual implementation type of the media stream. The latter is provided by an [[library implementation|StreamTypeImplFacade]]. While there are some common predefined prototypes, mostly, they are defined within the concrete [[Session]] according to the user's needs. 
 
@@ -3402,8 +3409,8 @@ Within the Proc-Layer, media streams are treated largely in a similar manner. Bu
 
This ID is an symbolic key linked to a StreamTypeDescriptor. The predicate {{{stream(ID)}}} specifies a media stream with the StreamType as detailed by the corresponding descriptor (which may contain complete or partial data defining the type).
-
-
A special kind of media stream [[implementation type|StreamTypeImplFacade]], which is not fully specified. As such, it is supposed there //actually is// an concrete implementation type, while only caring for some part or detail of this implementation to exhibit a specific property. For example, using an type constraint we can express the requirement of the actual implementation of a video stream to be based on RGB-float, or to enforce a fixed frame size in pixels.
+
+
A special kind of media stream [[implementation type|StreamTypeImplFacade]], which is not fully specified. As such, it is supposed there //actually is// an concrete implementation type, while only caring for some part or detail of this implementation to exhibit a specific property. For example, using an type constraint we can express the requirement of the actual implementation of a video stream to be based on ~RGB-float, or to enforce a fixed frame size in pixels.
 
 An implementation constraint can //stand-in// for a completely specified implementation type (meaning it's a sub interface of the latter). But actually using it in this way may cause a call to the [[defaults manager|DefaultsImplementation]] to fill in any missing information. An example would be to call {{{createFrame()}}} on the type constraint object, which means being able to allocate memory to hold a data frame, with properties in compliance with the given type constraint. Of cousre, then we need to know all the properties of this stream type, which is where the defaults manager is queried. This allows session customisation to kick in, but may fail under certain cicumstances.
 
@@ -3419,9 +3426,9 @@ An implementation constraint can //stand-in// for a completely specified impleme //Note:// there is a sort-of "degraded" variant just requiring some &rarr; [[implementation constraint|StreamTypeImplConstraint]] to hold
-
+
Querying for media stream type information comes in various flavours
-* you may find a structural object (pipe, output, processing patten) associated with / able to deal with a certain stream type
+* you may want to find a structural object (pipe, output, processing patten) associated with / able to deal with a certain stream type
 * you may need a StreamTypeDescriptor for an existing stream given as implementation data
 * you may want to build or complete type information from partial specification.
 Mostly, those queries involve the ConfigRules system in some way or the other. The [[prototype-|StreamPrototype]] and [[implementation type|StreamTypeImplFacade]]-interfaces themselves are mostly a facade for issuing appropriate queries. Some objects (especially [[pipes|Pipe]]) are tied to a certain stream type and thus store a direct link to type information. Others are just associated with a type by virtue of the DefaultsManagement.
@@ -3439,7 +3446,7 @@ The general case of this query is //quite hairy,// because the solution is not n
 We can inject two different kinds of fallback solutions for this kind of query:
 * we can always build a "catch-all" prototype just based on the kind of media (e.g. {{{prototype(video).}}}). This should match with lowest priority
 * we can search for existing ~StreamTypes with the same impl type, or an impl type which is //equivalent convertible// (see &rarr; StreamConversion). 
-The latter case can yield multiple solutions, which isn't any problem, because the match is limited to classes of equivalent stream implementation, which would be subsumed under the same prototype anyway. Even if the registry holds different prototypes linked to the same implementation type, they would be convertible and thus could stand in for one another. Together this results in the implementation
+The latter case can yield multiple solutions, which isn't any problem, because the match is limited to classes of equivalent stream implementation, which would be subsumed under the same prototype anyway. Even if the registry holds different prototypes linked to the same implementation type, they would be convertible and thus could //stand-in// for one another. Together this results in the implementation
 # try to get a direct match to an existing impl type which has an associated (complete) ~StreamType, thus bypassing the ConfigRules system altogether
 # run a {{{Query<Prototype>}}} for the given implementation type
 # do the search within equivalence class as described above
@@ -3450,9 +3457,18 @@ The latter case can yield multiple solutions, which isn't any problem, because t
 __Situation 1__: given an partially specified ~StreamType (just an [[constraint|StreamTypeImplConstraint]])
 __Situation 2__: find an implementation for a given prototype (without any further impl type guidlines)
 Both cases have to go though the [[defaults manager|DefaultsManagement]] in some way, in order to give any default configuration a chance to kick in. This is //one of the most important use cases// of the defaults system: the ability to configure a default fromat for all streams with certain semantic classification. {{{prototype(video)}}} by default is RGBA 24bit non-interlaced for example.
-But after having queried the defaults system, there remains the problem to build a new solution (which will then automatically become a default
+But after having queried the defaults system, there remains the problem to build a new solution (which will then automatically become default for this case). To be more precise: invoking the defaults system (as implemented in Lumiera) means first searching through existing objects encountered as default, and then issuing an general query with the capabilities in question. This general query in turn is conducted by the query type handler and usually consists of first searching existing objects and then creating a new object to match the capabilities. But, as said, the details depend on the type (and are defined by the query handler installed for this type). Translated to our problem here in question, this means //we have to define the basic operations from which a type query handler can be built.// Thus, to start with, it's completely sufficient to wire a call to the DefaultsManagement and assume the current session configuration contains some rules to cover it. Plus being prepared for the query to fail (throw, that is).
+
+Later on this could be augmented by providing some search mechanisms:
+* search through existing stream type implementations (or a suitable pre filtered selection) and narrow down the possible result(s) by using the constraint as a filter. Obviously this requires support by the MediaImplLib facade for the implementation in question. (This covers __Situation 1__)
+* relate a protoype in question to the other existing prototypes and use the convertibility / subsumption as a filter mechanism. Finally pick an existing impl type which is linked to one of the prototypes found thus far.
+Essentially, we need a search mechanism for impltypes and prototypes. This search mechanism is best defined by rules itself, but needs some primitive operations on types, like ennumerating all registered types, filter those selections and match against a constraint.
 
 !query for an (complete) StreamType
+All situations discussed thus far can also occur wrapped into and triggered by a query for a complete type. Depending on what part is known, the missing bits will be queried. 
+Independent from these is another __Situation__ where we query for a type ''by ID''.
+* a simple symbolic ID can be found by searching through all existing stream types (Operation supported by the type registry within STypeManager)
+* a special ''classificating'' ID can be parsed into the components (media kind, prototype, impltype), resulting in sub searches for these.
 
From 1724e019ea87963a2091204a368bd8d52d9e290c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 6 Oct 2008 06:17:40 +0200 Subject: [PATCH 17/66] related it to the ConManager and the wiring requests --- src/proc/mobject/builder/conmanager.cpp | 16 +++---- src/proc/mobject/builder/conmanager.hpp | 17 ++++--- src/proc/mobject/builder/wiringrequest.hpp | 54 ++++++++++++++++++++++ wiki/renderengine.html | 30 ++++++++---- 4 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 src/proc/mobject/builder/wiringrequest.hpp diff --git a/src/proc/mobject/builder/conmanager.cpp b/src/proc/mobject/builder/conmanager.cpp index e32ecf92b..14e20a788 100644 --- a/src/proc/mobject/builder/conmanager.cpp +++ b/src/proc/mobject/builder/conmanager.cpp @@ -1,5 +1,5 @@ /* - ConManager - manages the creation of additional ProcNode connections for the Renderengine + ConManager - manages the creation of data/control connections when building the Renderengine Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -22,15 +22,13 @@ #include "proc/mobject/builder/conmanager.hpp" +#include "proc/control/stypemanager.hpp" -namespace mobject - { - - namespace builder - { - - - +namespace mobject { + namespace builder { + + + /** * TODO !!!!!!!!!!!!!!!!!! */ diff --git a/src/proc/mobject/builder/conmanager.hpp b/src/proc/mobject/builder/conmanager.hpp index 3cd75cc6d..1f3eb23b8 100644 --- a/src/proc/mobject/builder/conmanager.hpp +++ b/src/proc/mobject/builder/conmanager.hpp @@ -1,5 +1,5 @@ /* - CONMANAGER.hpp - manages the creation of additional ProcNode connections for the Renderengine + CONMANAGER.hpp - manages the creation of data/control connections when building the Renderengine Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -26,17 +26,20 @@ -namespace mobject - { - namespace builder - { - - +namespace mobject { + namespace builder { + + /** * Connection Manager: used to build the connections between render engine nodes * if these nodes need to cooperate besides the normal "data pull" operation. * Esp. the Connection Manager knows how to wire up the effect's parameters * with the corresponding ParamProviders (autmation) in the Session. + * Questions regarding the possibility of a media stream connection are + * delegated internally to the STypeManager. + * \par + * The primary service of the connection manager is to accept a wiring request + * and handle the details of establishing the necessary connections. */ class ConManager { diff --git a/src/proc/mobject/builder/wiringrequest.hpp b/src/proc/mobject/builder/wiringrequest.hpp new file mode 100644 index 000000000..1190daccb --- /dev/null +++ b/src/proc/mobject/builder/wiringrequest.hpp @@ -0,0 +1,54 @@ +/* + WIRINGREQUEST.hpp - (interface) the intention to make a data or control connection + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 MOBJECT_BUILDER_WIRINGREQUEST_H +#define MOBJECT_BUILDER_WIRINGREQUEST_H + + + +namespace mobject { + namespace builder { + + + /** + * A stateful value object denoting the wish to establish a link or connection + * between two entities. Used to organize the proper working of the build process. + * Wiring requests are first to be checked and can be deemed impossible to + * satisfy. Internally, wiring requests contain specific information about + * the objects to be connected. This information is exposed only to the + * ConManager, which is the facility actually wiring the connections. + */ + class WiringRequest + { + public: + /** + * TODO design sketch...... + */ + }; + + + + } // namespace mobject::builder + +} // namespace mobject +#endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index b115d0ce6..624a36e3a 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -928,13 +928,18 @@ TertiaryMid: #99a TertiaryDark: #667 Error: #f88
-
-
The Connection Manager is a __Facade__ for querying information and deriving decisions regarding various aspects of data streams and possible connections.
-* retrieve information about capabilities of a stream type given by ID
+
+
The Connection Manager is a service for wiring connections and for querying information and deriving decisions regarding various aspects of data streams and the possibility of connections. The purpose of the Connection Manager is to isolate the [[Builder]], which is client of this information and decision services, from the often convoluted details of type information and organizing a connection.
+
+!control connections
+my intention was that it would be sufficient for the builder to pass an connection request, and the Connection Manager will handle the details of establishing a control/parameter link.
+{{red{TODO: handling of parameter values, automation and control connections still need to be designed}}}
+
+!data connections
+Connecting data streams of differing type involves a StreamConversion. Mostly, this aspect is covered by the [[stream type system|StreamType]]. The intended implementation will rely partially on [[rules|ConfigRules]] to define automated conversions, while other parts need to be provided by hard wired logic. Thus, regarding data connections, the ConManager can be seen as a specialized Facade and will delegate to the &rarr; [[stream type manager|STypeManager]]
+* retrieve information about capabilities of a stream type given by ID 
 * decide if a connection is possible
 * retrieve a //strategy// for implementing a connection
-
-In the intended implementation, a good deal of this functionality will actually be implemented by [[rules|ConfigRules]], while other parts need to be provided by hard wired logic, at least as a fallback. Anyway, the purpose of the Connection Manager ois to isolate the [[Builder]], which is client of this information and decision services, from these details
 
@@ -3338,10 +3343,10 @@ if (oldText.indexOf("SplashScreen")==-1) * in a future version, it may also encapsulate the communication in a distributed render farm
-
-
Conversion of a media stream into a stream of another type is done by a processor module (plugin). The problem of finding such a module is closely related to the StreamType and especially [[problems of querying|StreamTypeQuery]] for such. There can be different kinds of conversions, and the existance or non-existance of such an conversion can influence the stream type classification.
+
+
Conversion of a media stream into a stream of another type is done by a processor module (plugin). The problem of finding such a module is closely related to the StreamType and especially [[problems of querying|StreamTypeQuery]] for such. (The builder uses a special Facade, the ConManager, to access this functionality). There can be different kinds of conversions, and the existance or non-existance of such an conversion can influence the stream type classification.
 * different //kinds of media// can be ''transformed'' into each other
-* stream types //subsumed// by a given prototype should be ''lossless convertible''
+* stream types //subsumed// by a given prototype should be ''lossless convertible'' and thus can be considered //equivalent.//
 * besides, between different stream //implementation types,// there can be a ''rendering'' (lossy conversion) &mdash; or no conversion at all.
 
@@ -4917,6 +4922,15 @@ If ''not processing'' we don't have any input buffers, instead we get our output Otherwise, in the default case of actually ''processing'' out output, we have to organize input buffers, allocate output buffers, call the {{{process()}}} function of the WiringDescriptor and finally release the input buffers.
+
+
{{red{This is an early draft as of 9/08}}}
+Wiring requests rather belong to the realm of the high-level model, but play an important role in the build process, because the result of "executing" a wiring request will be to establish an actual low-level data connection. Wiring requests will be created automatically in the course of the build process, but they can be created manually and attached to the media objects in the high-level model as a ''wiring plug'', which is a special kind of LocatingPin (&rarr; [[Placement]])
+
+Wiring requests are small stateful value objects. They will be collected, sorted and processed ordered, such as to finish the building and get a completely wired network of processing nodes. Obviously, there needs to be some reference or smart pointer to the objects to be wired, but this information remains opaque.
+
+&rarr; ConManager
+
+
The purpose of automation is to vary a parameter of some data processing instance in the course of time while rendering. Thus, automation encompasses all the variability within the render network //which is not a structural change.//
 

From 1bce7d4c38f991c10ede657d24d6532ed051520d Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 6 Oct 2008 07:26:43 +0200
Subject: [PATCH 18/66] define the next steps by test

---
 src/common/streamtype.hpp             |  2 +-
 tests/common/streamtypebasicstest.cpp | 50 +++++++++++++++++++++++----
 wiki/renderengine.html                |  5 +--
 3 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/src/common/streamtype.hpp b/src/common/streamtype.hpp
index 28c87bfa4..4f79969d7 100644
--- a/src/common/streamtype.hpp
+++ b/src/common/streamtype.hpp
@@ -71,7 +71,7 @@ namespace lumiera {
       
       
       Prototype const& prototype;
-      ImplFacade * implType;
+      ImplFacade * implType;       /////////////TODO: really by ptr???
       Usage intentionTag;
       
     };
diff --git a/tests/common/streamtypebasicstest.cpp b/tests/common/streamtypebasicstest.cpp
index 6097da0a7..a2c1332d0 100644
--- a/tests/common/streamtypebasicstest.cpp
+++ b/tests/common/streamtypebasicstest.cpp
@@ -29,13 +29,15 @@
 
 #include 
 using std::cout;
-
+using ::test::Test;
+using util::isnil;
 
 
 namespace lumiera {
   namespace test_format {
     
     using control::STypeManager;
+    typedef StreamType const& SType;
     typedef StreamType::ImplFacade const& ImplType;
     
     
@@ -47,13 +49,18 @@ namespace lumiera {
      */
     class StreamTypeBasics_test : public Test
       {
-        virtual void run (Arg arg)
+        virtual void
+        run (Arg arg)
           {
-            buildImplType ();
-            basicImplTypeProperties ();
+            ImplType iType = buildImplType ();
+            basicImplTypeProperties (iType);
+            
+            SType type = extend2fullType (iType);
+            basicStreamTypeProperties (type, iType);
           }
         
-        void buildImplType ()
+        ImplType
+        buildImplType ()
           {
             STypeManager& typeManager = STypeManager::instance();
         
@@ -64,16 +71,45 @@ namespace lumiera {
             
             TODO ("how to do a simple consistency check on the returned ImplFacade? can we re-create the GAVL frame type?");
             ASSERT (GAVL==iTy.libraryID);
+            return iTy;
           }
         
-        void basicImplTypeProperties ()
+        void
+        basicImplTypeProperties (ImplType refType)
           {
-            ImplType iTy = test_createImplType ();
+            ImplType iTy2 = test_createImplType ();
+            ASSERT (iTy2==refType);
+            ASSERT (refType==iTy2);
+            TODO ("add equality comparable concept to the ImplType class");
             
+            ASSERT (StreamType::VIDEO==refType.getKind());
             UNIMPLEMENTED ("get a lib descriptor"); 
             UNIMPLEMENTED ("check the lib of the type"); 
             UNIMPLEMENTED ("compare two types"); 
           }
+        
+        SType
+        extend2fullType (ImplType iTy)
+          {
+            return STypeManager::instance().getType(iTy);
+          }
+        
+        void
+        basicStreamTypeProperties (SType type, ImplType iTy)
+          {
+            ASSERT (type.implType);
+            ASSERT (iTy==(*type.implType));  /////////////TODO: really by ptr???
+            ASSERT (&iTy==type.implType);   // actually using the same object (in the registry)
+            
+            ASSERT (!isnil (type.prototype.id));
+            ASSERT (StreamType::VIDEO==type.prototype.kind);
+            ASSERT (StreamType::VIDEO==type.implType->getKind());
+            
+            ASSERT (type.implType->canConvert(iTy));  // of course... they are actually the same
+            ASSERT (iTy.canConvert(type));           // because it's based on the same impl type
+            
+            ASSERT (StreamType::RAW==type.intentionTag);
+          }
       };
     
     LAUNCHER (StreamTypeBasics_test, "unit common");
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 624a36e3a..bb22c2320 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -3431,7 +3431,7 @@ An implementation constraint can //stand-in// for a completely specified impleme
 //Note:// there is a sort-of "degraded" variant just requiring some &rarr; [[implementation constraint|StreamTypeImplConstraint]] to hold
 
-
+
Querying for media stream type information comes in various flavours
 * you may want to find a structural object (pipe, output, processing patten) associated with / able to deal with a certain stream type
 * you may need a StreamTypeDescriptor for an existing stream given as implementation data
@@ -3471,9 +3471,10 @@ Essentially, we need a search mechanism for impltypes and prototypes. This searc
 
 !query for an (complete) StreamType
 All situations discussed thus far can also occur wrapped into and triggered by a query for a complete type. Depending on what part is known, the missing bits will be queried. 
-Independent from these is another __Situation__ where we query for a type ''by ID''.
+Independent from these is __another Situation__ where we query for a type ''by ID''.
 * a simple symbolic ID can be found by searching through all existing stream types (Operation supported by the type registry within STypeManager)
 * a special ''classificating'' ID can be parsed into the components (media kind, prototype, impltype), resulting in sub searches for these.
+{{red{not sure if we want to support queries by symboic ID}}}...problem is the impl type, because probably the library needs to support describing any implementation type by a string. Seemingly GAVL does, but requiring it for every lib?
 
From f777f05ab8cbda356a4cf68546c9eeef1f8c64bb Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 15 Oct 2008 00:05:44 +0200 Subject: [PATCH 19/66] plugin.c got removed from lib, let configure check for luid.c --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 0ef7ffd00..4df50f245 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_INIT(lumiera, 0.1pre) -AC_CONFIG_SRCDIR(src/lib/plugin.c) +AC_CONFIG_SRCDIR(src/lib/luid.c) AC_CONFIG_AUX_DIR(scripts) AM_INIT_AUTOMAKE AC_PREREQ(2.59) From 14a9e95492475df7d7420e1da4c85912f690ea32 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 10 Sep 2008 16:32:59 +0200 Subject: [PATCH 20/66] moved plugin code from lib to backend Plugin management will become stateful. This qualifies it to become a backend subsystem. --- Makefile.am | 1 - src/backend/Makefile.am | 6 ++-- src/{lib => backend}/plugin.c | 0 src/{lib => backend}/plugin.h | 0 src/lib/Makefile.am | 2 -- tests/{plugin => backend}/example_plugin.c | 0 tests/{plugin => backend}/example_plugin.cpp | 0 tests/{plugin => backend}/hello_interface.h | 0 tests/{plugin => backend}/plugin_main.c | 0 tests/plugin/DIR_INFO | 3 -- tests/plugin/Makefile.am | 37 -------------------- 11 files changed, 4 insertions(+), 45 deletions(-) rename src/{lib => backend}/plugin.c (100%) rename src/{lib => backend}/plugin.h (100%) rename tests/{plugin => backend}/example_plugin.c (100%) rename tests/{plugin => backend}/example_plugin.cpp (100%) rename tests/{plugin => backend}/hello_interface.h (100%) rename tests/{plugin => backend}/plugin_main.c (100%) delete mode 100644 tests/plugin/DIR_INFO delete mode 100644 tests/plugin/Makefile.am diff --git a/Makefile.am b/Makefile.am index f3ae3ef41..d236fbfd8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -62,7 +62,6 @@ include $(top_srcdir)/icons/Makefile.am include $(top_srcdir)/tests/common/Makefile.am include $(top_srcdir)/tests/components/Makefile.am include $(top_srcdir)/tests/Makefile.am -include $(top_srcdir)/tests/plugin/Makefile.am #EXTRA_DIST += admin debian doc depcomp README.BUILD LICENSE \ # cinelerra-cvs-current.spec diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index abb576021..ce63e44de 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -23,6 +23,7 @@ liblumibackend_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Werror liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/mediaaccessfacade.cpp \ $(liblumibackend_a_srcdir)/backend.c \ + $(liblumibackend_a_srcdir)/plugin.c \ $(liblumibackend_a_srcdir)/file.c \ $(liblumibackend_a_srcdir)/filehandle.c \ $(liblumibackend_a_srcdir)/filedescriptor.c \ @@ -30,19 +31,20 @@ liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/config.c \ $(liblumibackend_a_srcdir)/config_typed.c \ $(liblumibackend_a_srcdir)/configentry.c \ - $(liblumibackend_a_srcdir)/configitem.c \ + $(liblumibackend_a_srcdir)/configitem.c \ $(liblumibackend_a_srcdir)/config_lookup.c noinst_HEADERS += \ $(liblumibackend_a_srcdir)/mediaaccessfacade.cpp \ $(liblumibackend_a_srcdir)/backend.h \ + $(liblumibackend_a_srcdir)/plugin.h \ $(liblumibackend_a_srcdir)/file.h \ $(liblumibackend_a_srcdir)/filehandle.h \ $(liblumibackend_a_srcdir)/filedescriptor.h \ $(liblumibackend_a_srcdir)/filehandlecache.h \ $(liblumibackend_a_srcdir)/config.h \ $(liblumibackend_a_srcdir)/configentry.h \ - $(liblumibackend_a_srcdir)/configitem.h \ + $(liblumibackend_a_srcdir)/configitem.h \ $(liblumibackend_a_srcdir)/config_lookup.h diff --git a/src/lib/plugin.c b/src/backend/plugin.c similarity index 100% rename from src/lib/plugin.c rename to src/backend/plugin.c diff --git a/src/lib/plugin.h b/src/backend/plugin.h similarity index 100% rename from src/lib/plugin.h rename to src/backend/plugin.h diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index e7c22abdb..eab9b1465 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -21,7 +21,6 @@ noinst_LIBRARIES += liblumi.a liblumi_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Werror liblumi_a_SOURCES = \ - $(liblumi_a_srcdir)/plugin.c \ $(liblumi_a_srcdir)/error.c \ $(liblumi_a_srcdir)/mutex.c \ $(liblumi_a_srcdir)/rwlock.c \ @@ -35,7 +34,6 @@ liblumi_a_SOURCES = \ $(liblumi_a_srcdir)/appconfig.cpp noinst_HEADERS += \ - $(liblumi_a_srcdir)/plugin.h \ $(liblumi_a_srcdir)/error.h \ $(liblumi_a_srcdir)/mutex.h \ $(liblumi_a_srcdir)/rwlock.h \ diff --git a/tests/plugin/example_plugin.c b/tests/backend/example_plugin.c similarity index 100% rename from tests/plugin/example_plugin.c rename to tests/backend/example_plugin.c diff --git a/tests/plugin/example_plugin.cpp b/tests/backend/example_plugin.cpp similarity index 100% rename from tests/plugin/example_plugin.cpp rename to tests/backend/example_plugin.cpp diff --git a/tests/plugin/hello_interface.h b/tests/backend/hello_interface.h similarity index 100% rename from tests/plugin/hello_interface.h rename to tests/backend/hello_interface.h diff --git a/tests/plugin/plugin_main.c b/tests/backend/plugin_main.c similarity index 100% rename from tests/plugin/plugin_main.c rename to tests/backend/plugin_main.c diff --git a/tests/plugin/DIR_INFO b/tests/plugin/DIR_INFO deleted file mode 100644 index c051168cc..000000000 --- a/tests/plugin/DIR_INFO +++ /dev/null @@ -1,3 +0,0 @@ -working example code for Lumiera's plugin system -This directory contains example code which shows how to use specific features. -All examples will be build and run as part of the testsuite diff --git a/tests/plugin/Makefile.am b/tests/plugin/Makefile.am deleted file mode 100644 index c8cf9a22f..000000000 --- a/tests/plugin/Makefile.am +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (C) Lumiera.org -# 2007, Christian Thaeter -# -# 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. - -examples_srcdir = $(top_srcdir)/tests/plugin -noinst_PROGRAMS += test-plugin - -test_plugin_CFLAGS = $(AM_CFLAGS) -std=gnu99 -Wall -Werror -test_plugin_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -test_plugin_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -test_plugin_SOURCES = $(examples_srcdir)/plugin_main.c - -noinst_HEADERS += $(examples_srcdir)/hello_interface.h - -check_LTLIBRARIES += example_plugin.la example_plugin_cpp.la -example_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -example_plugin_la_SOURCES = $(examples_srcdir)/example_plugin.c -# the -rpath option is required, prolly a automake bug? -example_plugin_la_LDFLAGS = -avoid-version -module -rpath $(shell pwd) - -example_plugin_cpp_la_CPPFLAGS = $(AM_CPPFLAGS) -Wall -Werror -example_plugin_cpp_la_SOURCES = $(examples_srcdir)/example_plugin.cpp -# the -rpath option is required, prolly a automake bug? -example_plugin_cpp_la_LDFLAGS = -avoid-version -module -rpath $(shell pwd) From 973348fdb82a19979aa74399443311a2949cf7aa Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sat, 13 Sep 2008 10:42:54 +0200 Subject: [PATCH 21/66] preprocessor metaprogramming ftw Added a header for generalized preprocessor idioms. This will grow over time, as needed things will be added. --- src/lib/ppmpl.h | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/lib/ppmpl.h diff --git a/src/lib/ppmpl.h b/src/lib/ppmpl.h new file mode 100644 index 000000000..62de24104 --- /dev/null +++ b/src/lib/ppmpl.h @@ -0,0 +1,84 @@ +/* + ppmpl.h - preprocessor meta programming library + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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 PPMPL_H +#define PPMPL_H + +/** + * @file + * Preprocessor metaprogramming library. We define some useful preprocessor + * tricks here. + */ + + +/** + * Iterate over a list of macros. + * @param p used to disambiguate up to three passes, use _, _P1_ or _P2_ + * @param ... list of macros to be expanded. The user has to supply a definition + * in the form of PPMPL_FOREACH##p##macroname which shall expand to the desired text. + * + * This user defined macro shall be undefed after use. + * + * @example + * #define PPMPL_FOREACH_P1_FOO(arg) arg, + * + * {PPMPL_FOREACH(P1, FOO(1), FOO(2), FOO(3)), -1} + * + * #undef PPMPL_FOREACH_P1_FOO + * + * Would expand to the sequence: + * {1, 2, 3, -1} + */ +#define PPMPL_FOREACH(p, ...) PPMPL_FOREACH0(p, __VA_ARGS__, PPMPL_FOREACH_NIL)) + +/* internal */ +#define PPMPL_FOREACH0(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH1(p, __VA_ARGS__) +#define PPMPL_FOREACH1(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH2(p, __VA_ARGS__) +#define PPMPL_FOREACH2(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH3(p, __VA_ARGS__) +#define PPMPL_FOREACH3(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH4(p, __VA_ARGS__) +#define PPMPL_FOREACH4(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH5(p, __VA_ARGS__) +#define PPMPL_FOREACH5(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH6(p, __VA_ARGS__) +#define PPMPL_FOREACH6(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH7(p, __VA_ARGS__) +#define PPMPL_FOREACH7(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH8(p, __VA_ARGS__) +#define PPMPL_FOREACH8(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH9(p, __VA_ARGS__) +#define PPMPL_FOREACH9(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH10(p, __VA_ARGS__) +#define PPMPL_FOREACH10(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH11(p, __VA_ARGS__) +#define PPMPL_FOREACH11(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH12(p, __VA_ARGS__) +#define PPMPL_FOREACH12(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH13(p, __VA_ARGS__) +#define PPMPL_FOREACH13(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH14(p, __VA_ARGS__) +#define PPMPL_FOREACH14(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH15(p, __VA_ARGS__) +#define PPMPL_FOREACH15(p, h, ...) PPMPL_FOREACH##p##h +#define PPMPL_FOREACH_ +#define PPMPL_FOREACH_P1_ +#define PPMPL_FOREACH_P2_ +#define PPMPL_FOREACH_PPMPL_FOREACH_NIL PPMPL_FOREACH_FINAL( +#define PPMPL_FOREACH_P1_PPMPL_FOREACH_NIL PPMPL_FOREACH_FINAL( +#define PPMPL_FOREACH_P2_PPMPL_FOREACH_NIL PPMPL_FOREACH_FINAL( +#define PPMPL_FOREACH_FINAL(...) + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ From 4d65f0394d0f705ffd0332ace350e9ae2b8661ec Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sun, 14 Sep 2008 11:15:30 +0200 Subject: [PATCH 22/66] Add FOREACH variants for nesting to ppmpl Three levels of nesting are enough for anyone! --- src/lib/ppmpl.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/lib/ppmpl.h b/src/lib/ppmpl.h index 62de24104..f1553ccd0 100644 --- a/src/lib/ppmpl.h +++ b/src/lib/ppmpl.h @@ -46,6 +46,10 @@ * * Would expand to the sequence: * {1, 2, 3, -1} + * + * One can not recursively nest preprocessor macros. To allow this we define PPMPL_FOREACH_L1 + * to PPMPL_FOREACH_L2 with the same semantics as PPMPL_FOREACH, This allowes to nest the + * FOREACH loop up to three nesting levels. */ #define PPMPL_FOREACH(p, ...) PPMPL_FOREACH0(p, __VA_ARGS__, PPMPL_FOREACH_NIL)) @@ -74,6 +78,67 @@ #define PPMPL_FOREACH_P2_PPMPL_FOREACH_NIL PPMPL_FOREACH_FINAL( #define PPMPL_FOREACH_FINAL(...) + + + +#define PPMPL_FOREACH_L1(p, ...) PPMPL_FOREACH_L1_0(p, __VA_ARGS__, PPMPL_FOREACH_L1_NIL)) + +/* internal */ +#define PPMPL_FOREACH_L1_0(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_1(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_1(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_2(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_2(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_3(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_3(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_4(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_4(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_5(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_5(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_6(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_6(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_7(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_7(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_8(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_8(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_9(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_9(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_10(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_10(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_11(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_11(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_12(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_12(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_13(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_13(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_14(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_14(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_15(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_15(p, h, ...) PPMPL_FOREACH_L1##p##h +#define PPMPL_FOREACH_L1_ +#define PPMPL_FOREACH_L1_P1_ +#define PPMPL_FOREACH_L1_P2_ +#define PPMPL_FOREACH_L1_PPMPL_FOREACH_L1_NIL PPMPL_FOREACH_L1_FINAL( +#define PPMPL_FOREACH_L1_P1_PPMPL_FOREACH_L1_NIL PPMPL_FOREACH_L1_FINAL( +#define PPMPL_FOREACH_L1_P2_PPMPL_FOREACH_L1_NIL PPMPL_FOREACH_L1_FINAL( +#define PPMPL_FOREACH_L1_FINAL(...) + + + + +#define PPMPL_FOREACH_L2(p, ...) PPMPL_FOREACH_L2_0(p, __VA_ARGS__, PPMPL_FOREACH_L2_NIL)) + +/* internal */ +#define PPMPL_FOREACH_L2_0(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_1(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_1(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_2(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_2(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_3(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_3(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_4(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_4(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_5(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_5(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_6(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_6(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_7(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_7(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_8(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_8(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_9(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_9(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_10(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_10(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_11(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_11(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_12(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_12(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_13(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_13(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_14(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_14(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_15(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_15(p, h, ...) PPMPL_FOREACH_L2##p##h +#define PPMPL_FOREACH_L2_ +#define PPMPL_FOREACH_L2_P1_ +#define PPMPL_FOREACH_L2_P2_ +#define PPMPL_FOREACH_L2_PPMPL_FOREACH_L2_NIL PPMPL_FOREACH_L2_FINAL( +#define PPMPL_FOREACH_L2_P1_PPMPL_FOREACH_L2_NIL PPMPL_FOREACH_L2_FINAL( +#define PPMPL_FOREACH_L2_P2_PPMPL_FOREACH_L2_NIL PPMPL_FOREACH_L2_FINAL( +#define PPMPL_FOREACH_L2_FINAL(...) + + #endif /* // Local Variables: From 16d2fcf2e95531dd07d8eab6e4d572b684fa9d15 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sun, 14 Sep 2008 19:06:20 +0200 Subject: [PATCH 23/66] macro for initializing a uchar[16] from a string literal in C++ --- src/lib/luid.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib/luid.h b/src/lib/luid.h index a93d004a3..553d22765 100644 --- a/src/lib/luid.h +++ b/src/lib/luid.h @@ -31,6 +31,22 @@ */ typedef unsigned char lumiera_uid[16]; +typedef lumiera_uid* LumieraUid; + +/* + C++ can't initialize arrays from string literals with the trailing \0 cropped + C can't initialize non constant members, + there we go +*/ +#ifdef __cplusplus +#define LUMIERA_UID_INITIALIZER(l) \ + { \ + l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], \ + l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] \ + } +#else +#define LUMIERA_UID_INITIALIZER(l) l +#endif /** * Retrieve a generic pointer stored in a luid From 7d863679096f66e3b90fe71e2b106f4fe9162a98 Mon Sep 17 00:00:00 2001 From: Nicholas Sinnott-Armstrong Date: Mon, 15 Sep 2008 18:45:05 -0400 Subject: [PATCH 24/66] Added preliminary support for chained mutex calls. --- src/lib/mutex.h | 33 +++++++++++++++++++++++++++++++++ tests/15locking.tests | 6 ++++++ tests/library/test-locking.c | 22 +++++++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/lib/mutex.h b/src/lib/mutex.h index 681d29432..0de69c2f7 100644 --- a/src/lib/mutex.h +++ b/src/lib/mutex.h @@ -60,6 +60,39 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); } \ })) +/** + * Mutual exclusion chainbuilder section. + * Usage: LUMIERA_MUTEX_SECTION(a){LUMIERA_MUTEX_SECTION_CHAIN(a,b){run();}} + * calls lock(a); lock(b); unlock(a); run(); unlock(b); + * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be + * called on the correct mutexes, period. + */ +#define LUMIERA_MUTEX_SECTION_CHAIN(nobugflag, mtxa, mtxb) \ + for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtxb); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtxb)->rh, "acquire mutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtxb)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + if (mtxa) \ + { \ + pthread_mutex_unlock (&mtxa->mutex); \ + mtxa = NULL; \ + RESOURCE_LEAVE(nobugflag, mtxa.rh); \ + } \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ + })) /** * Mutex. diff --git a/tests/15locking.tests b/tests/15locking.tests index c7e8340e5..fab6431bf 100644 --- a/tests/15locking.tests +++ b/tests/15locking.tests @@ -18,6 +18,12 @@ out: inner mutex locked section END +TEST "chained mutex section" chainedmutexsection < Date: Tue, 16 Sep 2008 15:15:34 +0200 Subject: [PATCH 25/66] add a static initializer to psplay.h --- src/lib/psplay.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/psplay.h b/src/lib/psplay.h index 26639910a..b3c016c52 100644 --- a/src/lib/psplay.h +++ b/src/lib/psplay.h @@ -51,6 +51,7 @@ struct psplaynode_struct PSplaynode right; }; +#define PSPLAYNODE_INITIALIZER {NULL, NULL} /** * Function use to compare keys From a7f75b3f6f5ed4513c4e8461a096b2d7b5b71621 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 16 Sep 2008 15:16:39 +0200 Subject: [PATCH 26/66] preprocessor concat implementation which evaluates its arguments --- src/lib/ppmpl.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/ppmpl.h b/src/lib/ppmpl.h index f1553ccd0..3a175c322 100644 --- a/src/lib/ppmpl.h +++ b/src/lib/ppmpl.h @@ -138,6 +138,11 @@ #define PPMPL_FOREACH_L2_P2_PPMPL_FOREACH_L2_NIL PPMPL_FOREACH_L2_FINAL( #define PPMPL_FOREACH_L2_FINAL(...) +/** + * Canonical preprocessor concat implementation which evaluates its arguments + */ +#define PPMPL_CAT(a,b) PPMPL_CAT_(a,b) +#define PPMPL_CAT_(a,b) a##b #endif /* From 2a723bc5bae63b5d842942491d25655413a7dcce Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 19 Sep 2008 15:39:12 +0200 Subject: [PATCH 27/66] add 'STRINGIFY' to ppmpl.h --- src/lib/ppmpl.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/ppmpl.h b/src/lib/ppmpl.h index 3a175c322..edc7bec7c 100644 --- a/src/lib/ppmpl.h +++ b/src/lib/ppmpl.h @@ -144,6 +144,10 @@ #define PPMPL_CAT(a,b) PPMPL_CAT_(a,b) #define PPMPL_CAT_(a,b) a##b + +#define PPMPL_STRINGIFY(a) PPMPL_STRINGIFY_(a) +#define PPMPL_STRINGIFY_(a) #a + #endif /* // Local Variables: From 9cdfd02e9b8d5f242c4f8c6ff765f8cfe93bd871 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 19 Sep 2008 15:46:25 +0200 Subject: [PATCH 28/66] makefile update for library --- src/lib/Makefile.am | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index eab9b1465..0b42926ad 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -20,17 +20,19 @@ noinst_LIBRARIES += liblumi.a liblumi_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Werror -liblumi_a_SOURCES = \ - $(liblumi_a_srcdir)/error.c \ - $(liblumi_a_srcdir)/mutex.c \ - $(liblumi_a_srcdir)/rwlock.c \ - $(liblumi_a_srcdir)/condition.c \ - $(liblumi_a_srcdir)/luid.c \ - $(liblumi_a_srcdir)/safeclib.c \ - $(liblumi_a_srcdir)/cuckoo.c \ - $(liblumi_a_srcdir)/psplay.c \ - $(liblumi_a_srcdir)/mrucache.c \ - $(liblumi_a_srcdir)/time.c \ +liblumi_a_SOURCES = \ + $(liblumi_a_srcdir)/error.c \ + $(liblumi_a_srcdir)/mutex.c \ + $(liblumi_a_srcdir)/rwlock.c \ + $(liblumi_a_srcdir)/condition.c \ + $(liblumi_a_srcdir)/luid.c \ + $(liblumi_a_srcdir)/safeclib.c \ + $(liblumi_a_srcdir)/cuckoo.c \ + $(liblumi_a_srcdir)/psplay.c \ + $(liblumi_a_srcdir)/mrucache.c \ + $(liblumi_a_srcdir)/time.c \ + $(liblumi_a_srcdir)/interface.c \ + $(liblumi_a_srcdir)/interfacedescriptor.c \ $(liblumi_a_srcdir)/appconfig.cpp noinst_HEADERS += \ @@ -44,6 +46,9 @@ noinst_HEADERS += \ $(liblumi_a_srcdir)/psplay.h \ $(liblumi_a_srcdir)/mrucache.h \ $(liblumi_a_srcdir)/time.h \ + $(liblumi_a_srcdir)/ppmpl.h \ + $(liblumi_a_srcdir)/interface.h \ + $(liblumi_a_srcdir)/interfacedescriptor.h \ $(liblumi_a_srcdir)/appconfig.hpp \ $(liblumi_a_srcdir)/lifecycleregistry.hpp From ed246ab2224667db40faa8021da2bfa61063ac38 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sat, 27 Sep 2008 04:19:43 +0200 Subject: [PATCH 29/66] Adding recursive mutexes, fix chained mutex to take only one parameter Recursive mutex can be locked multiple times by a single thread they are initialitzed by lumiera_recmutex_init() and used by LUMIERA_RECMUTEX_* macros. Chained mutex use the mutexacquirer from the outer scope now. Maybe its later needed to pass acquirers explicit, we will see. --- src/lib/mutex.c | 24 +++++++++ src/lib/mutex.h | 97 +++++++++++++++++++++++++++++++----- tests/15locking.tests | 6 +++ tests/library/test-locking.c | 23 ++++++++- 4 files changed, 136 insertions(+), 14 deletions(-) diff --git a/src/lib/mutex.c b/src/lib/mutex.c index 99ef8897f..b228de746 100644 --- a/src/lib/mutex.c +++ b/src/lib/mutex.c @@ -44,6 +44,30 @@ lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* f } +static pthread_once_t recursive_mutexattr_once = PTHREAD_ONCE_INIT; +static pthread_mutexattr_t recursive_mutexattr; + +static void recursive_mutexattr_init() +{ + pthread_mutexattr_init (&recursive_mutexattr); + pthread_mutexattr_settype (&recursive_mutexattr, PTHREAD_MUTEX_RECURSIVE); +} + + +LumieraMutex +lumiera_recmutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag) +{ + if (self) + { + pthread_once(&recursive_mutexattr_once, recursive_mutexattr_init); + pthread_mutex_init (&self->mutex, &recursive_mutexattr); + NOBUG_RESOURCE_HANDLE_INIT (self->rh); + NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "recmutex", purpose, self, self->rh); + } + return self; +} + + LumieraMutex lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag) { diff --git a/src/lib/mutex.h b/src/lib/mutex.h index 0de69c2f7..9645cfd35 100644 --- a/src/lib/mutex.h +++ b/src/lib/mutex.h @@ -62,27 +62,28 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); /** * Mutual exclusion chainbuilder section. - * Usage: LUMIERA_MUTEX_SECTION(a){LUMIERA_MUTEX_SECTION_CHAIN(a,b){run();}} + * Usage: LUMIERA_MUTEX_SECTION(a){LUMIERA_MUTEX_SECTION_CHAIN(b){run();}} * calls lock(a); lock(b); unlock(a); run(); unlock(b); * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be * called on the correct mutexes, period. */ -#define LUMIERA_MUTEX_SECTION_CHAIN(nobugflag, mtxa, mtxb) \ - for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ +#define LUMIERA_MUTEX_SECTION_CHAIN(nobugflag, mtx) \ + for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ + NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ lumiera_mutex_section_.mutex;) \ for ( \ ({ \ - lumiera_mutex_section_.mutex = (mtxb); \ + lumiera_mutex_section_.mutex = (mtx); \ NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtxb)->rh, "acquire mutex", &lumiera_mutex_section_, \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtxb)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - if (mtxa) \ - { \ - pthread_mutex_unlock (&mtxa->mutex); \ - mtxa = NULL; \ - RESOURCE_LEAVE(nobugflag, mtxa.rh); \ - } \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + if (lumiera_mutex_section_old_->mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ + lumiera_mutex_section_old_->mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ + } \ }); \ lumiera_mutex_section_.mutex; \ ({ \ @@ -94,6 +95,68 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); } \ })) + +/** + * Recursive Mutual exclusive section. + */ +#define LUMIERA_RECMUTEX_SECTION(nobugflag, mtx) \ + for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ + })) + + +/** + * Mutual exclusion chainbuilder section. + * Usage: LUMIERA_MUTEX_SECTION(a){LUMIERA_RECMUTEX_SECTION_CHAIN(b){run();}} + * calls lock(a); lock(b); unlock(a); run(); unlock(b); + * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be + * called on the correct mutexes, period. + */ +#define LUMIERA_RECMUTEX_SECTION_CHAIN(nobugflag, mtx) \ + for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ + NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + if (lumiera_mutex_section_old_->mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ + lumiera_mutex_section_old_->mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ + } \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ + })) + + /** * Mutex. * @@ -109,12 +172,22 @@ typedef lumiera_mutex* LumieraMutex; /** * Initialize a mutex variable + * This initializes a 'fast' default mutex which must not be locked recursively from one thread. * @param self is a pointer to the mutex to be initialized * @return self as given */ LumieraMutex lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag); +/** + * Initialize a mutex variable + * Initializes a 'recursive' mutex which might be locked by the same thread multiple times. + * @param self is a pointer to the mutex to be initialized + * @return self as given + */ +LumieraMutex +lumiera_recmutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag); + /** * Destroy a mutex variable diff --git a/tests/15locking.tests b/tests/15locking.tests index fab6431bf..3420f3fa1 100644 --- a/tests/15locking.tests +++ b/tests/15locking.tests @@ -24,6 +24,12 @@ out: inner but not outer mutex locked section END +TEST "recursive mutex section" recursivemutexsection < Date: Sun, 28 Sep 2008 03:49:28 +0200 Subject: [PATCH 30/66] use a recursive mutex to lock interface operations locking interfaceregistry operations and later on open/close etc --- src/backend/interfaceregistry.c | 206 ++++++++++++++++++++++++++++++++ src/backend/interfaceregistry.h | 77 ++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 src/backend/interfaceregistry.c create mode 100644 src/backend/interfaceregistry.h diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c new file mode 100644 index 000000000..89d26fe60 --- /dev/null +++ b/src/backend/interfaceregistry.c @@ -0,0 +1,206 @@ +/* + interfaceregistry.c - Lumiera interface registry + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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/mutex.h" +#include "lib/error.h" +#include "lib/psplay.h" + + +#include + + + +#include "backend/interfaceregistry.h" + +/** + * @file + * Interface instances are published and activated by registering them + * into a global registry, which is defined here. This instances are identified + * by their name and major version. + */ + +NOBUG_DEFINE_FLAG_PARENT (interfaceregistry, backend); + +static PSplay interfaceregistry; +lumiera_mutex lumiera_interface_mutex; + + +static int +cmp_fn (const void* keya, const void* keyb); + +static const void* +key_fn (const PSplaynode node); + + + +/** + * Initialize the interface registry + */ +void +lumiera_interfaceregistry_init (void) +{ + NOBUG_INIT_FLAG (interfaceregistry); + TRACE (interfaceregistry); + REQUIRE (!interfaceregistry); + + interfaceregistry = psplay_new (cmp_fn, key_fn, NULL); + if (!interfaceregistry) + LUMIERA_DIE (ERRNO); + + lumiera_recmutex_init (&lumiera_interface_mutex, "interfaceregistry", &NOBUG_FLAG(interfaceregistry)); +} + +void +lumiera_interfaceregistry_destroy (void) +{ + TRACE (interfaceregistry); + REQUIRE (!psplay_nelements (interfaceregistry)); + + lumiera_mutex_destroy (&lumiera_interface_mutex, &NOBUG_FLAG(interfaceregistry)); + + if (interfaceregistry) + psplay_destroy (interfaceregistry); + interfaceregistry = NULL; +} + + +void +lumiera_interfaceregistry_register_interface (LumieraInterface self) +{ + TRACE (interfaceregistry); + REQUIRE (self); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + TRACE (interfaceregistry, "interface %s, version %d, instance %s", self->interface, self->version, self->name); + psplay_insert (interfaceregistry, &self->node, 100); + } +} + + +void +lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self) +{ + TRACE (interfaceregistry); + REQUIRE (self); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + while (*self) + { + psplay_insert (interfaceregistry, &(*self)->node, 100); + ++self; + } + } +} + + +void +lumiera_interfaceregistry_remove_interface (LumieraInterface self) +{ + TRACE (interfaceregistry); + REQUIRE (self); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + psplay_remove (interfaceregistry, &self->node); + } +} + + +void +lumiera_interfaceregistry_bulkremove_interfaces (LumieraInterface* self) +{ + TRACE (interfaceregistry); + REQUIRE (self); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + while (*self) + { + psplay_remove (interfaceregistry, &(*self)->node); + ++self; + } + } +} + + +LumieraInterface +lumiera_interfaceregistry_interface_find (const char* interface, unsigned version, const char* name) +{ + TRACE (interfaceregistry); + struct lumiera_interface_struct cmp; + cmp.interface = interface; + cmp.version = version; + cmp.name = name; + + LumieraInterface ret = NULL; + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + ret = (LumieraInterface)psplay_find (interfaceregistry, &cmp, 100); + } + + return ret; +} + + +static int +cmp_fn (const void* keya, const void* keyb) +{ + const LumieraInterface a = (const LumieraInterface)keya; + const LumieraInterface b = (const LumieraInterface)keyb; + + int r = strcmp (a->interface, b->interface); + if (r<0) + return -1; + else if (r>0) + return 1; + + if (a->version < b->version) + return -1; + else if (a->version > b->version) + return 1; + + r = strcmp (a->name, b->name); + if (r<0) + return -1; + else if (r>0) + return 1; + + return 0; +} + + +static const void* +key_fn (const PSplaynode node) +{ + return node; +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/interfaceregistry.h b/src/backend/interfaceregistry.h new file mode 100644 index 000000000..20f765c90 --- /dev/null +++ b/src/backend/interfaceregistry.h @@ -0,0 +1,77 @@ +/* + interfaceregistry.h - Lumiera interface registry + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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 LUMIERA_INTERFACEREGISTRY_H +#define LUMIERA_INTERFACEREGISTRY_H + +#include "lib/mutex.h" +#include "lib/interface.h" + +#include + + +/** + * @file + * Interface instances are published and activated by registering them + * into a gloabl registry, which is defined here. This instances are identified + * by their name and major version. + */ + +NOBUG_DECLARE_FLAG (interfaceregistry); + +extern lumiera_mutex lumiera_interface_mutex; + + +/** + * Initialize the interface registry + */ +void +lumiera_interfaceregistry_init (void); + +void +lumiera_interfaceregistry_destroy (void); + + +void +lumiera_interfaceregistry_register_interface (LumieraInterface self); + +void +lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self); + +void +lumiera_interfaceregistry_remove_interface (LumieraInterface self); + +void +lumiera_interfaceregistry_bulkremove_interfaces (LumieraInterface* self); + +LumieraInterface +lumiera_interfaceregistry_interface_find (const char* interface, unsigned version, const char* name); + + + + +#endif /* LUMIERA_INTERFACEREGISTRY_H */ +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ From 38f2a9c6c90161ff3a4ee36490de4e7f3708914a Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 9 Oct 2008 18:28:44 +0200 Subject: [PATCH 31/66] add a lumiera_realloc() function to the safeclib --- src/lib/safeclib.c | 11 +++++++++++ src/lib/safeclib.h | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/lib/safeclib.c b/src/lib/safeclib.c index 1c5cf28c6..7f8f79eb2 100644 --- a/src/lib/safeclib.c +++ b/src/lib/safeclib.c @@ -51,6 +51,17 @@ lumiera_calloc (size_t n, size_t size) } +void* +lumiera_realloc (void* ptr, size_t size) +{ + void* o = size ? realloc (ptr, size) : NULL; + if (!o) + LUMIERA_DIE (NO_MEMORY); + + return o; +} + + char* lumiera_strndup (const char* str, size_t len) { diff --git a/src/lib/safeclib.h b/src/lib/safeclib.h index 79f10d024..d949595f8 100644 --- a/src/lib/safeclib.h +++ b/src/lib/safeclib.h @@ -50,6 +50,16 @@ void* lumiera_calloc (size_t n, size_t size); +/** + * Change the size of a memory block. + * @param ptr pointer to the old memory block obtained by lumiera_malloc or lumiera_calloc + * @param size new size of the block + * @return address of new block + */ +void* +lumiera_realloc (void* ptr, size_t size); + + /** * Free previously allocated memory. * @param mem pointer to the memory block obtained by lumiera_malloc or lumiera_calloc From dd9e5051baeb46e40602bc25127a62640348a10f Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 10 Oct 2008 00:56:13 +0200 Subject: [PATCH 32/66] use nobug's new RESOURCE_HANDLE_COMMA_INITIALIZER and some cosmetics along --- src/lib/mutex.h | 148 +++++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 72 deletions(-) diff --git a/src/lib/mutex.h b/src/lib/mutex.h index 9645cfd35..86fb48944 100644 --- a/src/lib/mutex.h +++ b/src/lib/mutex.h @@ -39,25 +39,26 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); /** * Mutual exclusive section. */ -#define LUMIERA_MUTEX_SECTION(nobugflag, mtx) \ - for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ - lumiera_mutex_section_.mutex;) \ - for ( \ - ({ \ - lumiera_mutex_section_.mutex = (mtx); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - }); \ - lumiera_mutex_section_.mutex; \ - ({ \ - if (lumiera_mutex_section_.mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ - lumiera_mutex_section_.mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ - } \ +#define LUMIERA_MUTEX_SECTION(nobugflag, mtx) \ + for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ \ + = {(LumieraMutex)1 NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ })) /** @@ -67,32 +68,33 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be * called on the correct mutexes, period. */ -#define LUMIERA_MUTEX_SECTION_CHAIN(nobugflag, mtx) \ - for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ - NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ - lumiera_mutex_section_.mutex;) \ - for ( \ - ({ \ - lumiera_mutex_section_.mutex = (mtx); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - if (lumiera_mutex_section_old_->mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ - lumiera_mutex_section_old_->mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ - } \ - }); \ - lumiera_mutex_section_.mutex; \ - ({ \ - if (lumiera_mutex_section_.mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ - lumiera_mutex_section_.mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ - } \ +#define LUMIERA_MUTEX_SECTION_CHAIN(nobugflag, mtx) \ + for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ + NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ + NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + if (lumiera_mutex_section_old_->mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ + lumiera_mutex_section_old_->mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ + } \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ })) @@ -100,7 +102,8 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); * Recursive Mutual exclusive section. */ #define LUMIERA_RECMUTEX_SECTION(nobugflag, mtx) \ - for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ + for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ + NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ lumiera_mutex_section_.mutex;) \ for ( \ ({ \ @@ -128,32 +131,33 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be * called on the correct mutexes, period. */ -#define LUMIERA_RECMUTEX_SECTION_CHAIN(nobugflag, mtx) \ - for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ - NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ - lumiera_mutex_section_.mutex;) \ - for ( \ - ({ \ - lumiera_mutex_section_.mutex = (mtx); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - if (lumiera_mutex_section_old_->mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ - lumiera_mutex_section_old_->mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ - } \ - }); \ - lumiera_mutex_section_.mutex; \ - ({ \ - if (lumiera_mutex_section_.mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ - lumiera_mutex_section_.mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ - } \ +#define LUMIERA_RECMUTEX_SECTION_CHAIN(nobugflag, mtx) \ + for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ + NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ + NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + if (lumiera_mutex_section_old_->mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ + lumiera_mutex_section_old_->mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ + } \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ })) From f754e1521f4145fbad6ff05bc4c228299d0324d9 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 22 Sep 2008 22:38:17 +0200 Subject: [PATCH 33/66] Start of a 'wordlist' type for the config subsystem wordlists are simple not quoted words delimited by semicolon, tab, space or commas. Some special functions will allow to access each of this words by index etc. --- src/backend/Makefile.am | 1 + src/backend/config.h | 7 ++- src/backend/config_wordlist.c | 95 ++++++++++++++++++++++++++++++++++ tests/22config_highlevel.tests | 22 ++++++++ tests/backend/test-config.c | 21 ++++++++ 5 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/backend/config_wordlist.c diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index ce63e44de..2cde41ae2 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -30,6 +30,7 @@ liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/filehandlecache.c \ $(liblumibackend_a_srcdir)/config.c \ $(liblumibackend_a_srcdir)/config_typed.c \ + $(liblumibackend_a_srcdir)/config_wordlist.c \ $(liblumibackend_a_srcdir)/configentry.c \ $(liblumibackend_a_srcdir)/configitem.c \ $(liblumibackend_a_srcdir)/config_lookup.c diff --git a/src/backend/config.h b/src/backend/config.h index 80f33a6d5..a603c2cbf 100644 --- a/src/backend/config.h +++ b/src/backend/config.h @@ -96,6 +96,7 @@ typedef lumiera_config* LumieraConfig; LUMIERA_CONFIG_TYPE(number, signed long long) \ LUMIERA_CONFIG_TYPE(real, long double) \ LUMIERA_CONFIG_TYPE(string, const char*) \ + LUMIERA_CONFIG_TYPE(wordlist, const char*) \ LUMIERA_CONFIG_TYPE(word, const char*) \ LUMIERA_CONFIG_TYPE(bool, int) @@ -223,7 +224,11 @@ lumiera_config_setdefault (const char* line); LUMIERA_CONFIG_TYPES #undef LUMIERA_CONFIG_TYPE - +/** + * special functions for accessing wordlists + */ +const char* +lumiera_config_wordlist_get_nth (const char* key, unsigned nth); // * {{{ lumiera_config_TYPE_set (const char* key, TYPE*value, const char* fmt) }}} // Highlevel interface for different types, fmt is a printf format specifier for the desired format, when NULL, defaults apply. diff --git a/src/backend/config_wordlist.c b/src/backend/config_wordlist.c new file mode 100644 index 000000000..3bcbf1ca8 --- /dev/null +++ b/src/backend/config_wordlist.c @@ -0,0 +1,95 @@ +/* + config_wordlist.c - Lumiera wordlist access functions + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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. +*/ + +//TODO: Support library includes// +#include "lib/error.h" +#include "lib/safeclib.h" + + +//TODO: Lumiera header includes// +#include "backend/config.h" + +//TODO: internal/static forward declarations// + + +//TODO: System includes// + +/** + * return nth word of a wordlist + */ +const char* +lumiera_config_wordlist_get_nth (const char* key, unsigned nth) +{ + const char* value; + size_t len; + + if (!lumiera_config_wordlist_get (key, &value)) + return NULL; + + for (;;) + { + value += strspn (value, " \t,;"); + len = strcspn (value, " \t,;"); + if (!nth && *value) + break; + + --nth; + value += len; + + if (!*value) + return NULL; + } + + return lumiera_tmpbuf_strndup (value, len); +} + +#if 0 +int +lumiera_config_wordlist_find (const char* key, const char* value) +{ +} + + +LumieraConfigitem +lumiera_config_wordlist_set_nth (const char* key, const char** value, unsigned nth) +{ +} + +LumieraConfigitem +lumiera_config_wordlist_append (const char* key, const char** value, unsigned nth) +{ +} + +LumieraConfigitem +lumiera_config_wordlist_add (const char* key, const char** value, unsigned nth) +{ +} +#endif + + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/tests/22config_highlevel.tests b/tests/22config_highlevel.tests index e0e4995c8..42e2561f3 100644 --- a/tests/22config_highlevel.tests +++ b/tests/22config_highlevel.tests @@ -192,3 +192,25 @@ out: item->key_size = '7' out: item->key = 'key.foo < key.bar' out: item->delim = '< key.bar' END + + +TEST "wordlist get item from empty list should fail" wordlist_get_nth 'foo.bar' '' 0 << END +out: 'NULL' +END + +TEST "wordlist get item past end should fail" wordlist_get_nth 'foo.bar' 'baz barf gnarf' 3 << END +out: 'NULL' +END + +TEST "wordlist get first item" wordlist_get_nth 'foo.bar' 'baz barf gnarf' 0 << END +out: 'baz' +END + +TEST "wordlist get last item" wordlist_get_nth 'foo.bar' 'baz barf; gnarf' 2 << END +out: 'gnarf' +END + +TEST "wordlist get middle" wordlist_get_nth 'foo.bar' 'baz barf, gnarf' 1 << END +out: 'barf' +END + diff --git a/tests/backend/test-config.c b/tests/backend/test-config.c index ea312a429..2ed4f4234 100644 --- a/tests/backend/test-config.c +++ b/tests/backend/test-config.c @@ -287,4 +287,25 @@ TEST ("configitem_simple_content_check") lumiera_config_destroy (); } + +TEST ("wordlist_get_nth") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + + lumiera_config_init ("./"); + + if (!lumiera_config_wordlist_set (argv[2], &argv[3])) + printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); + + const char* word = lumiera_config_wordlist_get_nth (argv[2], atoi (argv[4])); + + printf ("'%s'\n", word?word:"NULL"); + + lumiera_config_destroy (); +} + + + TESTS_END From 3f273bb0b10eb2eb5dc57ab8b7bdeae051d2bf20 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 25 Sep 2008 00:07:35 +0200 Subject: [PATCH 34/66] move the configitem tests to the lowlevel suite --- tests/20config_lowlevel.tests | 62 +++++++++++++++++++++++++++++++++ tests/22config_highlevel.tests | 63 ---------------------------------- 2 files changed, 62 insertions(+), 63 deletions(-) diff --git a/tests/20config_lowlevel.tests b/tests/20config_lowlevel.tests index 1bebedcbc..795ed11d8 100644 --- a/tests/20config_lowlevel.tests +++ b/tests/20config_lowlevel.tests @@ -21,6 +21,68 @@ out: delim = '=' out: value = ' baz' END +TEST "create configitem with empty line" configitem_simple_ctor_dtor '' <line = ' #comment bla' +END + +TEST "check content of configitem with section" configitem_simple_content_check $'[ key.foo suffix.bar ] ' << END +out: item->line = '[ key.foo suffix.bar ] ' +out: item->key_size = '7' +out: item->key = 'key.foo suffix.bar ] ' +out: item->delim = ' suffix.bar ] ' +END + +TEST "check content of configitem with directive (without argument)" configitem_simple_content_check $'\t @directive ' << END +out: item->line = ' @directive ' +out: item->key_size = '9' +out: item->key = '@directive ' +END + +TEST "check content of configitem with directive (with argument)" configitem_simple_content_check $'\t @directive \targument' << END +out: item->line = ' @directive argument' +out: item->key_size = '9' +out: item->key = '@directive argument' +out: item->delim = ' argument' +END + +TEST "check content of configitem with configentry" configitem_simple_content_check $' \t\t key.foo \t\t=\tbar' << END +out: item->line = ' key.foo = bar' +out: item->key_size = '7' +out: item->key = 'key.foo = bar' +out: item->delim = '= bar' +END + +TEST "check content of configitem with configentry (redirect)" configitem_simple_content_check $' \t\t key.foo \t\t<\tkey.bar' << END +out: item->line = ' key.foo < key.bar' +out: item->key_size = '7' +out: item->key = 'key.foo < key.bar' +out: item->delim = '< key.bar' +END + TEST "set a config and retrieve it" basic_set_get 'foo' '=bar' <line = ' #comment bla' -END - -TEST "check content of configitem with section" configitem_simple_content_check $'[ key.foo suffix.bar ] ' << END -out: item->line = '[ key.foo suffix.bar ] ' -out: item->key_size = '7' -out: item->key = 'key.foo suffix.bar ] ' -out: item->delim = ' suffix.bar ] ' -END - -TEST "check content of configitem with directive (without argument)" configitem_simple_content_check $'\t @directive ' << END -out: item->line = ' @directive ' -out: item->key_size = '9' -out: item->key = '@directive ' -END - -TEST "check content of configitem with directive (with argument)" configitem_simple_content_check $'\t @directive \targument' << END -out: item->line = ' @directive argument' -out: item->key_size = '9' -out: item->key = '@directive argument' -out: item->delim = ' argument' -END - -TEST "check content of configitem with configentry" configitem_simple_content_check $' \t\t key.foo \t\t=\tbar' << END -out: item->line = ' key.foo = bar' -out: item->key_size = '7' -out: item->key = 'key.foo = bar' -out: item->delim = '= bar' -END - -TEST "check content of configitem with configentry (redirect)" configitem_simple_content_check $' \t\t key.foo \t\t<\tkey.bar' << END -out: item->line = ' key.foo < key.bar' -out: item->key_size = '7' -out: item->key = 'key.foo < key.bar' -out: item->delim = '< key.bar' -END - - TEST "wordlist get item from empty list should fail" wordlist_get_nth 'foo.bar' '' 0 << END out: 'NULL' END From 7d748a39a7ab0b15adb2c50917964961cc6e7b81 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 25 Sep 2008 00:09:00 +0200 Subject: [PATCH 35/66] add a test for value resetting (I wasn't sure it works, but it does) --- tests/20config_lowlevel.tests | 5 +++++ tests/backend/test-config.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/tests/20config_lowlevel.tests b/tests/20config_lowlevel.tests index 795ed11d8..bcc8e71e3 100644 --- a/tests/20config_lowlevel.tests +++ b/tests/20config_lowlevel.tests @@ -21,6 +21,11 @@ out: delim = '=' out: value = ' baz' END +TEST "reset a value" change_value foo.bar '=foo' '=bar' < Date: Thu, 25 Sep 2008 17:29:00 +0200 Subject: [PATCH 36/66] more on config_wordlist * universal replace function * tests * missing declarations --- src/backend/config.h | 6 +++ src/backend/config_typed.c | 47 ++++++++++++++++++ src/backend/config_wordlist.c | 87 +++++++++++++++++++++++++++++++++- tests/22config_highlevel.tests | 51 ++++++++++++++++++++ tests/backend/test-config.c | 44 +++++++++++++++++ 5 files changed, 233 insertions(+), 2 deletions(-) diff --git a/src/backend/config.h b/src/backend/config.h index a603c2cbf..5f711ae0e 100644 --- a/src/backend/config.h +++ b/src/backend/config.h @@ -230,6 +230,12 @@ LUMIERA_CONFIG_TYPES const char* lumiera_config_wordlist_get_nth (const char* key, unsigned nth); +int +lumiera_config_wordlist_find (const char* key, const char* value); + +const char* +lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2); + // * {{{ lumiera_config_TYPE_set (const char* key, TYPE*value, const char* fmt) }}} // Highlevel interface for different types, fmt is a printf format specifier for the desired format, when NULL, defaults apply. /** diff --git a/src/backend/config_typed.c b/src/backend/config_typed.c index ae490ff2b..aec8bbc68 100644 --- a/src/backend/config_typed.c +++ b/src/backend/config_typed.c @@ -228,6 +228,53 @@ lumiera_config_string_set (const char* key, const char** value) } +/** + * Wordlist + * words delimited by any of " \t,;" + */ + +const char* +lumiera_config_wordlist_get (const char* key, const char** value) +{ + TRACE (config_typed, "KEY %s", key); + + const char* raw_value = *value = NULL; + + LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + { + if (lumiera_config_get (key, &raw_value)) + { + if (raw_value) + { + *value = raw_value; + } + else + LUMIERA_ERROR_SET (config, CONFIG_NO_ENTRY); + + TODO ("remove the ERROR_SET because config_get sets it already? also in all other getters in this file"); + } + } + + return *value; +} + + +LumieraConfigitem +lumiera_config_wordlist_set (const char* key, const char** value) +{ + TRACE (config_typed); + + LumieraConfigitem item = NULL; + + LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + { + const char* fmt = "= %s"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); + item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, *value)); + } + + return item; +} + /** * Word diff --git a/src/backend/config_wordlist.c b/src/backend/config_wordlist.c index 3bcbf1ca8..cbaa68d86 100644 --- a/src/backend/config_wordlist.c +++ b/src/backend/config_wordlist.c @@ -28,6 +28,7 @@ #include "backend/config.h" //TODO: internal/static forward declarations// +extern LumieraConfig lumiera_global_config; //TODO: System includes// @@ -61,15 +62,91 @@ lumiera_config_wordlist_get_nth (const char* key, unsigned nth) return lumiera_tmpbuf_strndup (value, len); } -#if 0 + int lumiera_config_wordlist_find (const char* key, const char* value) { + const char* itr; + size_t vlen = strlen (value); + size_t len; + + if (!lumiera_config_wordlist_get (key, &itr)) + return -1; + + for (int idx = 0; *itr; itr += len, ++idx) + { + itr += strspn (itr, " \t,;"); + len = strcspn (itr, " \t,;"); + + if (len == vlen && !strncmp (itr, value, vlen)) + return idx; + } + + return -1; } +const char* +lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2) +{ + const char* wordlist; + const char* str = NULL; + size_t vlen = strlen (value); + size_t len; + + LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + { + if (lumiera_config_get (key, &wordlist)) + { + const char* start = wordlist + strspn (wordlist, " \t,;"); + + for (const char* itr = start; *itr; itr += len) + { + const char* left_end = itr; + itr += strspn (itr, " \t,;"); + len = strcspn (itr, " \t,;"); + + if (len == vlen && !strncmp (itr, value, vlen)) + { + TODO ("figure delimiter from original string out"); + const char* delim = " "; + + /* step over the word */ + itr += len; + itr += strspn (itr, " \t,;"); + + /* getting the delimiters right for the corner cases looks ugly, want to refactor it? just do it */ + str = lumiera_tmpbuf_snprintf (SIZE_MAX, + "%.*s%.*s%s%s%s%s%s%s", + start - wordlist, wordlist, + left_end - start, start, + (left_end - start && subst1 && *subst1) ? delim : "", + (subst1 && *subst1) ? subst1 : "", + ((left_end - start || (subst1 && *subst1)) && subst2 && *subst2) ? delim : "", + (subst2 && *subst2) ? subst2 : "", + ((left_end - start || (subst1 && *subst1) || (subst2 && *subst2)) && *itr) ? delim : "", + itr + ); + + if (!lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, "=%s", str))) + str = NULL; + + break; + } + } + } + } + + return str; +} + + + +#if 0 + + LumieraConfigitem -lumiera_config_wordlist_set_nth (const char* key, const char** value, unsigned nth) +lumiera_config_wordlist_remove_nth (const char* key, const char** value, unsigned nth) { } @@ -78,6 +155,12 @@ lumiera_config_wordlist_append (const char* key, const char** value, unsigned nt { } +LumieraConfigitem +lumiera_config_wordlist_preprend (const char* key, const char** value, unsigned nth) +{ +} + + LumieraConfigitem lumiera_config_wordlist_add (const char* key, const char** value, unsigned nth) { diff --git a/tests/22config_highlevel.tests b/tests/22config_highlevel.tests index 0349fa8fe..e2400c6ff 100644 --- a/tests/22config_highlevel.tests +++ b/tests/22config_highlevel.tests @@ -151,3 +151,54 @@ TEST "wordlist get middle" wordlist_get_nth 'foo.bar' 'baz barf, gnarf' 1 << END out: 'barf' END +TEST "wordlist find, non existing" wordlist_find 'foo.bar' 'baz barf, gnarf' blah << END +out: '-1' +END + +TEST "wordlist find, first" wordlist_find 'foo.bar' 'baz barf, gnarf' baz << END +out: '0' +END + +TEST "wordlist find, middle" wordlist_find 'foo.bar' 'baz barf, gnarf' barf << END +out: '1' +END + +TEST "wordlist find, last" wordlist_find 'foo.bar' 'baz barf, gnarf' gnarf << END +out: '2' +END + +TEST "wordlist replace, middle, insert after" wordlist_replace 'foo.bar' 'baz barf gnarf' barf barf foof << END +out: ' baz barf foof gnarf' +END + +TEST "wordlist replace, middle, insert before" wordlist_replace 'foo.bar' 'baz barf gnarf' barf foof barf << END +out: ' baz foof barf gnarf' +END + +TEST "wordlist replace, middle, remove" wordlist_replace 'foo.bar' 'baz barf gnarf' barf '' '' << END +out: ' baz gnarf' +END + +TEST "wordlist replace, middle, replace1" wordlist_replace 'foo.bar' 'baz barf gnarf' barf 'foof' '' << END +out: ' baz foof gnarf' +END + +TEST "wordlist replace, middle, replace2" wordlist_replace 'foo.bar' 'baz barf gnarf' barf '' 'foof' << END +out: ' baz foof gnarf' +END + +TEST "wordlist replace, first, insert before" wordlist_replace 'foo.bar' 'baz barf gnarf' baz 'first' 'baz' << END +out: ' first baz barf gnarf' +END + +TEST "wordlist replace, first, replace1" wordlist_replace 'foo.bar' 'baz barf gnarf' baz 'first' '' << END +out: ' first barf gnarf' +END + +TEST "wordlist replace, first, replace2" wordlist_replace 'foo.bar' 'baz barf gnarf' baz '' 'first' << END +out: ' first barf gnarf' +END + +TEST "wordlist replace, last, insert after" wordlist_replace 'foo.bar' 'baz barf gnarf' gnarf 'gnarf' 'last' << END +out: ' baz barf gnarf last' +END diff --git a/tests/backend/test-config.c b/tests/backend/test-config.c index 8f0e5597e..89d6d94ad 100644 --- a/tests/backend/test-config.c +++ b/tests/backend/test-config.c @@ -288,6 +288,7 @@ TEST ("configitem_simple_ctor_dtor") lumiera_config_destroy (); } + TEST ("configitem_simple_content_check") { REQUIRE (argv[2]); @@ -337,5 +338,48 @@ TEST ("wordlist_get_nth") } +TEST ("wordlist_find") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + + lumiera_config_init ("./"); + + if (!lumiera_config_wordlist_set (argv[2], &argv[3])) + printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); + + int n = lumiera_config_wordlist_find (argv[2], argv[4]); + + printf ("'%d'\n", n); + + lumiera_config_destroy (); +} + + +TEST ("wordlist_replace") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + REQUIRE (argv[5]); + REQUIRE (argv[6]); + + lumiera_config_init ("./"); + + if (!lumiera_config_wordlist_set (argv[2], &argv[3])) + printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); + + const char* wordlist = lumiera_config_wordlist_replace (argv[2], argv[4], *argv[5]?argv[5]:NULL, *argv[6]?argv[6]:NULL); + + if (wordlist) + printf ("'%s'\n", wordlist); + else + printf ("%s\n", lumiera_error ()); + + lumiera_config_destroy (); +} + + TESTS_END From b0e60e23e0ae94c789e975684374d5866f8734bc Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 25 Sep 2008 17:34:44 +0200 Subject: [PATCH 37/66] change config rwlock to mutex This was overseen with the splay tree transistion. lookups mutate the structure, thus it needs to be locked completely. --- src/backend/config.c | 6 +++--- src/backend/config.h | 13 +++---------- src/backend/config_typed.c | 16 ++++++++-------- src/backend/config_wordlist.c | 2 +- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/backend/config.c b/src/backend/config.c index 2e724be08..8c917eb8e 100644 --- a/src/backend/config.c +++ b/src/backend/config.c @@ -111,7 +111,7 @@ lumiera_config_init (const char* path) lumiera_configitem_init (&lumiera_global_config->files); lumiera_configitem_init (&lumiera_global_config->TODO_unknown); - lumiera_rwlock_init (&lumiera_global_config->lock, "config rwlock", &NOBUG_FLAG (config)); + lumiera_mutex_init (&lumiera_global_config->lock, "config mutex", &NOBUG_FLAG (config)); lumiera_config_setdefault (lumiera_tmpbuf_snprintf (SIZE_MAX, "config.path = %s", path)); @@ -130,7 +130,7 @@ lumiera_config_destroy () TRACE (config); if (lumiera_global_config) { - lumiera_rwlock_destroy (&lumiera_global_config->lock, &NOBUG_FLAG (config)); + lumiera_mutex_destroy (&lumiera_global_config->lock, &NOBUG_FLAG (config)); lumiera_configitem_destroy (&lumiera_global_config->defaults, &lumiera_global_config->keys); lumiera_configitem_destroy (&lumiera_global_config->files, &lumiera_global_config->keys); lumiera_configitem_destroy (&lumiera_global_config->TODO_unknown, &lumiera_global_config->keys); @@ -290,7 +290,7 @@ lumiera_config_setdefault (const char* line) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config, &lumiera_global_config->lock) { const char* key = line; while (*key && isspace (*key)) diff --git a/src/backend/config.h b/src/backend/config.h index 5f711ae0e..b87f8e2bf 100644 --- a/src/backend/config.h +++ b/src/backend/config.h @@ -24,7 +24,7 @@ //TODO: Support library includes// #include "lib/error.h" -#include "lib/rwlock.h" +#include "lib/mutex.h" //TODO: Forward declarations// struct lumiera_config_struct; @@ -74,14 +74,7 @@ struct lumiera_config_struct lumiera_configitem files; /* all loaded files */ lumiera_configitem TODO_unknown; /* all values which are not part of a file and not default TODO: this will be removed when file support is finished */ - /* - all access is protected with rwlock's. - We use rwlocks here since concurrent reads are likely common. - - So far this is a global config lock, if this is a problem we might granularize it by locking on a file level. - config access is not planned to be transactional yet, if this is a problem we need to expose the rwlock to a config_acquire/config_release function pair - */ - lumiera_rwlock lock; + lumiera_mutex lock; }; typedef struct lumiera_config_struct lumiera_config; @@ -198,7 +191,7 @@ lumiera_config_set (const char* key, const char* delim_value); * Installs a default value for a config key. * Any key might have an associated default value which is used when * no other configuration is available, this can be set once. - * Any subsequent call will be a no-op. This function writelocks the config system. + * Any subsequent call will be a no-op. This function locks the config system. * @param line line with key, delimiter and value to store as default value * @return NULL in case of an error, else a pointer to the default configitem */ diff --git a/src/backend/config_typed.c b/src/backend/config_typed.c index aec8bbc68..d17ed77a3 100644 --- a/src/backend/config_typed.c +++ b/src/backend/config_typed.c @@ -66,7 +66,7 @@ lumiera_config_number_get (const char* key, long long* value) const char* raw_value = NULL; - LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &raw_value)) { @@ -94,7 +94,7 @@ lumiera_config_number_set (const char* key, long long* value) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { const char* fmt = "= %lld"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, *value)); @@ -195,7 +195,7 @@ lumiera_config_string_get (const char* key, const char** value) const char* raw_value = *value = NULL; - LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &raw_value)) { @@ -218,7 +218,7 @@ lumiera_config_string_set (const char* key, const char** value) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { const char* fmt = "= %s"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, *value)); @@ -240,7 +240,7 @@ lumiera_config_wordlist_get (const char* key, const char** value) const char* raw_value = *value = NULL; - LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &raw_value)) { @@ -266,7 +266,7 @@ lumiera_config_wordlist_set (const char* key, const char** value) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { const char* fmt = "= %s"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, *value)); @@ -309,7 +309,7 @@ lumiera_config_word_get (const char* key, const char** value) const char* raw_value = *value = NULL; - LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &raw_value)) { @@ -332,7 +332,7 @@ lumiera_config_word_set (const char* key, const char** value) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { const char* fmt = "= %s"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, scan_word (*value))); diff --git a/src/backend/config_wordlist.c b/src/backend/config_wordlist.c index cbaa68d86..19378bedf 100644 --- a/src/backend/config_wordlist.c +++ b/src/backend/config_wordlist.c @@ -94,7 +94,7 @@ lumiera_config_wordlist_replace (const char* key, const char* value, const char* size_t vlen = strlen (value); size_t len; - LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &wordlist)) { From 211b470e85b4669b480a2edaf635ecb7feed6c02 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 25 Sep 2008 22:26:18 +0200 Subject: [PATCH 38/66] docing existing wordlist funcs --- src/backend/config.h | 34 +++++++++++++++++++++++++++++++++- src/backend/config_wordlist.c | 3 --- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/backend/config.h b/src/backend/config.h index b87f8e2bf..96e519c0d 100644 --- a/src/backend/config.h +++ b/src/backend/config.h @@ -217,18 +217,50 @@ lumiera_config_setdefault (const char* line); LUMIERA_CONFIG_TYPES #undef LUMIERA_CONFIG_TYPE + /** - * special functions for accessing wordlists + * Wordlists + * Wordlists are lists of single words delimited by any of " \t,;". + * They can be used to store groups of keys and other kinds of simple references into the config + * system. Here are some functions to manipulate single word entries in a wordlist. + */ + +/** + * Get nth word of a wordlist. + * @param key key under which this wordlist is stored + * @param nth index of the word to get, starting with 0 + * @return pointer to a tempbuf holding the nth word or NULL in case of error */ const char* lumiera_config_wordlist_get_nth (const char* key, unsigned nth); + +/** + * Find the index of a word in a wordlist. + * @param key key under which this wordlist is stored + * @param value word to find + * @return index of the first occurence of the word or -1 in case of failure + */ int lumiera_config_wordlist_find (const char* key, const char* value); + +/** + * Universal word replacement function. + * Replaces a word with up to two new words. This can be used to delete a word (no replacements), + * insert a new word before an existing word (giving the new word as subst1 and the old word as subst2) + * insert a new word after an existing word (giving the old word as subst1 and the new word as subst2) + * or simply give 2 new words. + * @param key key under which this wordlist is stored + * @param value word to be replaced + * @param subst1 first replacement word + * @param subst2 second replacement word + * @return internal representation of the wordlist in a tmpbuf or NULL in case of an error + */ const char* lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2); + // * {{{ lumiera_config_TYPE_set (const char* key, TYPE*value, const char* fmt) }}} // Highlevel interface for different types, fmt is a printf format specifier for the desired format, when NULL, defaults apply. /** diff --git a/src/backend/config_wordlist.c b/src/backend/config_wordlist.c index 19378bedf..acee07562 100644 --- a/src/backend/config_wordlist.c +++ b/src/backend/config_wordlist.c @@ -33,9 +33,6 @@ extern LumieraConfig lumiera_global_config; //TODO: System includes// -/** - * return nth word of a wordlist - */ const char* lumiera_config_wordlist_get_nth (const char* key, unsigned nth) { From 2ae274ea51dbd2f9b1d7b6c7ceab6c6335202099 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 26 Sep 2008 03:31:22 +0200 Subject: [PATCH 39/66] add wordlist_add function to add a word to a wordlist --- src/backend/config.h | 9 ++++++ src/backend/config_wordlist.c | 54 +++++++++++++++++++++++++++++----- tests/22config_highlevel.tests | 20 +++++++++++++ tests/backend/test-config.c | 28 ++++++++++++++++++ 4 files changed, 104 insertions(+), 7 deletions(-) diff --git a/src/backend/config.h b/src/backend/config.h index 96e519c0d..0b50c7da2 100644 --- a/src/backend/config.h +++ b/src/backend/config.h @@ -261,6 +261,15 @@ const char* lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2); +/** + * Add a word to the end of a wordlist if it doesnt exist already + * @param key key under which this wordlist is stored + * @param value new word to add + * @return internal representation of the wordlist in a tmpbuf or NULL in case of an error + */ +const char* +lumiera_config_wordlist_add (const char* key, const char* value); + // * {{{ lumiera_config_TYPE_set (const char* key, TYPE*value, const char* fmt) }}} // Highlevel interface for different types, fmt is a printf format specifier for the desired format, when NULL, defaults apply. /** diff --git a/src/backend/config_wordlist.c b/src/backend/config_wordlist.c index acee07562..548a4d824 100644 --- a/src/backend/config_wordlist.c +++ b/src/backend/config_wordlist.c @@ -91,6 +91,9 @@ lumiera_config_wordlist_replace (const char* key, const char* value, const char* size_t vlen = strlen (value); size_t len; + if (!value) + return NULL; + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &wordlist)) @@ -138,15 +141,56 @@ lumiera_config_wordlist_replace (const char* key, const char* value, const char* } +const char* +lumiera_config_wordlist_add (const char* key, const char* value) +{ + const char* wordlist = NULL; + + if (value && *value) + { + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) + { + if (lumiera_config_get (key, &wordlist)) + { + size_t vlen = strlen (value); + size_t len; + + for (const char* itr = wordlist; *itr; itr += len) + { + itr += strspn (itr, " \t,;"); + len = strcspn (itr, " \t,;"); + + if (len == vlen && !strncmp (itr, value, vlen)) + goto end; + } + + TODO ("figure delimiter from original string out"); + const char* delim = " "; + + wordlist = lumiera_tmpbuf_snprintf (SIZE_MAX, "%s%s%s", + wordlist, + wordlist[strspn (wordlist, " \t,;")]?delim:"", + value); + + if (!lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, "=%s", wordlist))) + wordlist = NULL; + } + end:; + } + } + + return wordlist; +} + #if 0 - -LumieraConfigitem -lumiera_config_wordlist_remove_nth (const char* key, const char** value, unsigned nth) +const char* +lumiera_config_wordlist_remove_nth (const char* key, unsigned nth) { } + LumieraConfigitem lumiera_config_wordlist_append (const char* key, const char** value, unsigned nth) { @@ -158,10 +202,6 @@ lumiera_config_wordlist_preprend (const char* key, const char** value, unsigned } -LumieraConfigitem -lumiera_config_wordlist_add (const char* key, const char** value, unsigned nth) -{ -} #endif diff --git a/tests/22config_highlevel.tests b/tests/22config_highlevel.tests index e2400c6ff..3d131da3e 100644 --- a/tests/22config_highlevel.tests +++ b/tests/22config_highlevel.tests @@ -202,3 +202,23 @@ END TEST "wordlist replace, last, insert after" wordlist_replace 'foo.bar' 'baz barf gnarf' gnarf 'gnarf' 'last' << END out: ' baz barf gnarf last' END + +TEST "wordlist add 2 words" wordlist_add 'foo.bar' 'baz barf gnarf' first second << END +out: ' baz barf gnarf first' +out: ' baz barf gnarf first second' +END + +TEST "wordlist add same word" wordlist_add 'foo.bar' 'baz barf gnarf' same same << END +out: ' baz barf gnarf same' +out: ' baz barf gnarf same' +END + +TEST "wordlist add to empty list" wordlist_add 'foo.bar' '' first second << END +out: ' first' +out: ' first second' +END + +TEST "wordlist add to empty list, same" wordlist_add 'foo.bar' '' same same << END +out: ' same' +out: ' same' +END diff --git a/tests/backend/test-config.c b/tests/backend/test-config.c index 89d6d94ad..deffa7c00 100644 --- a/tests/backend/test-config.c +++ b/tests/backend/test-config.c @@ -381,5 +381,33 @@ TEST ("wordlist_replace") } +TEST ("wordlist_add") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + REQUIRE (argv[5]); + + lumiera_config_init ("./"); + + if (!lumiera_config_wordlist_set (argv[2], &argv[3])) + printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); + + const char* wordlist = lumiera_config_wordlist_add (argv[2], argv[4]); + if (wordlist) + printf ("'%s'\n", wordlist); + else + printf ("%s\n", lumiera_error ()); + + wordlist = lumiera_config_wordlist_add (argv[2], argv[5]); + if (wordlist) + printf ("'%s'\n", wordlist); + else + printf ("%s\n", lumiera_error ()); + + lumiera_config_destroy (); +} + + TESTS_END From fdbab22ce4072a81362561d174d0aeda44849ed6 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 13 Oct 2008 23:39:48 +0200 Subject: [PATCH 40/66] some cosmetics for the config subsystem --- src/backend/config.c | 6 ++++++ src/backend/config_typed.c | 6 ++++++ src/backend/configitem.c | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/backend/config.c b/src/backend/config.c index 8c917eb8e..cec0dbfd8 100644 --- a/src/backend/config.c +++ b/src/backend/config.c @@ -146,6 +146,7 @@ lumiera_config_destroy () int lumiera_config_load (const char* file) { + (void) file; TRACE (config); UNIMPLEMENTED(); return -1; @@ -164,6 +165,7 @@ lumiera_config_save () int lumiera_config_purge (const char* filename) { + (void) filename; TRACE (config); UNIMPLEMENTED(); @@ -342,6 +344,7 @@ lumiera_config_dump (FILE* out) int lumiera_config_reset (const char* key) { + (void) key; TRACE (config); UNIMPLEMENTED(); return -1; @@ -351,6 +354,9 @@ lumiera_config_reset (const char* key) int lumiera_config_info (const char* key, const char** filename, unsigned* line) { + (void) key; + (void) filename; + (void) line; TRACE (config); UNIMPLEMENTED(); return -1; diff --git a/src/backend/config_typed.c b/src/backend/config_typed.c index d17ed77a3..963090899 100644 --- a/src/backend/config_typed.c +++ b/src/backend/config_typed.c @@ -41,6 +41,7 @@ extern LumieraConfig lumiera_global_config; const char* lumiera_config_link_get (const char* key, const char** value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -49,6 +50,7 @@ lumiera_config_link_get (const char* key, const char** value) LumieraConfigitem lumiera_config_link_set (const char* key, const char** value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -111,6 +113,7 @@ lumiera_config_number_set (const char* key, long long* value) const char* lumiera_config_real_get (const char* key, long double* value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -119,6 +122,7 @@ lumiera_config_real_get (const char* key, long double* value) LumieraConfigitem lumiera_config_real_set (const char* key, long double* value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -349,6 +353,7 @@ lumiera_config_word_set (const char* key, const char** value) const char* lumiera_config_bool_get (const char* key, int* value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -358,6 +363,7 @@ lumiera_config_bool_get (const char* key, int* value) LumieraConfigitem lumiera_config_bool_set (const char* key, int* value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; diff --git a/src/backend/configitem.c b/src/backend/configitem.c index f3550da38..8015a6e20 100644 --- a/src/backend/configitem.c +++ b/src/backend/configitem.c @@ -241,8 +241,8 @@ lumiera_configitem_parse (LumieraConfigitem self, const char* line) itr += self->key_size; /*we need a key with a length greather than zero and - * either the end of the line - * or a whitespace after the key */ + * either end of line + * or whitespace after key */ if ( self->key_size && ( !*itr || (*itr && isspace(*itr)) )) { From de44b83fcd94a8af4414d69313844af8fd9f6b0d Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sun, 14 Sep 2008 19:10:44 +0200 Subject: [PATCH 41/66] New interface header Provides structures used for managing interfaces, macros for declaring and defining interfaces. Convinience macros to bundle buildin and plugin interfaces. --- src/lib/interface.h | 204 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/lib/interface.h diff --git a/src/lib/interface.h b/src/lib/interface.h new file mode 100644 index 000000000..bc114f7e7 --- /dev/null +++ b/src/lib/interface.h @@ -0,0 +1,204 @@ +/* + interface.h - Lumiera interface macros and structures + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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 LUMIERA_INTERFACE_H +#define LUMIERA_INTERFACE_H + +#include "lib/luid.h" +#include "lib/ppmpl.h" + +/* + Interface declaration macros + */ + +#define LUMIERA_INTERFACE_INAME(name, version) lumiera_interface_##name##_##version +#define LUMIERA_INTERFACE_DNAME(iname, dname, version) dname##_##version##_interface + +#define LUMIERA_INTERFACE_TYPE(name, version) struct LUMIERA_INTERFACE_INAME(name, version) +#define LUMIERA_INTERFACE_CAST(name, version) (LUMIERA_INTERFACE_TYPE(name, version)*) + + + +#define PPMPL_FOREACH_LUMIERA_INTERFACE_SLOT(ret, name, params) ret (*name) params; lumiera_uid name##_uid; + + +#define LUMIERA_INTERFACE_DECLARE(name, version, ...) \ +LUMIERA_INTERFACE_TYPE(name, version) \ +{ \ + lumiera_interface interface_header_; \ + PPMPL_FOREACH(_, __VA_ARGS__) \ +} + + +/* + Interface definition macros + */ + + +#ifdef __cplusplus +#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, uid) \ + function, LUMIERA_UID_INITIALIZER (uid), +#else +#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, uid) \ + .slot = function, .slot##_uid = LUMIERA_UID_INITIALIZER (uid), +#endif + + +/** + * + * + * + * + * + * + * + * + * + * + */ +#define LUMIERA_INTERFACE_INITIALIZER(iname, version, name, descriptor, data, acquire, release, ...) \ +{ \ + { \ + #name, \ + version, \ + sizeof (LUMIERA_INTERFACE_TYPE(iname, version)), \ + descriptor, \ + data, \ + acquire, \ + release \ + }, \ + PPMPL_FOREACH(_, __VA_ARGS__) \ +} + + +#define LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ +LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, version) = \ + LUMIERA_INTERFACE_INITIALIZER(iname, version, name, descriptor, data, acquire, release, __VA_ARGS__); + + + +#define PPMPL_FOREACH_L1_P1_LUMIERA_INTERFACE(iname, version, name, descriptor, data, acquire, release, ...) \ +LUMIERA_INTERFACE_DEFINE (iname, version, \ + name, \ + descriptor, \ + data, \ + acquire, \ + release, \ + __VA_ARGS__ \ + ); + + +#define PPMPL_FOREACH_L1_P2_LUMIERA_INTERFACE(iname, version, name, descriptor, data, acquire, release, ...) \ + &LUMIERA_INTERFACE_DNAME(iname, name, version).interface_header_, + + +#define LUMIERA_PLUGIN(descriptor, data, acquire, release, luid, ...) \ +PPMPL_FOREACH_L1(_P1_, __VA_ARGS__) \ +static const LumieraInterface* \ +plugin_interfaces (void) \ +{ \ + static const LumieraInterface interfaces[] = \ + { \ + PPMPL_FOREACH_L1(_P2_, __VA_ARGS__) \ + NULL \ + }; \ + return interfaces; \ +} \ +LUMIERA_INTERFACE_DEFINE (plugin, 0, \ + lumiera_plugin, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + LUMIERA_INTERFACE_MAP (plugin_interfaces, plugin_interfaces, luid) \ + ) + + + +#define LUMIERA_EXPORT(queryfunc, ...) \ +PPMPL_FOREACH_L1(_P1_, __VA_ARGS__) \ +static const LumieraInterface* \ +queryfunc (void) \ +{ \ + static const LumieraInterface interfaces[] = \ + { \ + PPMPL_FOREACH_L1(_P2_, __VA_ARGS__) \ + NULL \ + }; \ + return interfaces; \ +} \ + + +typedef struct lumiera_interfaceslot_struct lumiera_interfaceslot; +typedef lumiera_interfaceslot* LumieraInterfaceslot; + +typedef struct lumiera_interface_struct lumiera_interface; +typedef lumiera_interface* LumieraInterface; + +/** + * This is just a placeholder for an entry in a interface table. + * It consists of one here generic, later correctly prototyped functionppointer and + * a unique identifier which is associated with this function. + */ +struct lumiera_interfaceslot_struct +{ + void (*func)(void); + lumiera_uid uid; +}; + + +/** + * Header for an interface, just the absolute necessary metadata. + */ +struct lumiera_interface_struct +{ + /** name of the interface */ + const char* name; + /** major version, 0 means experimental */ + unsigned version; + /** size of the whole interface structure (minor version) */ + size_t size; + + /** metadata descriptor, itself a interface (or NULL) */ + LumieraInterface descriptor; + + /** generic data to be used by the implementation */ + void* data; + + /** must be called before this interface is used, might be nested */ + LumieraInterface (*acquire)(LumieraInterface self); + /** called when finished with this interface, must match the acquire calls */ + void (*release)(LumieraInterface self); + + /** placeholder array for the following function slots, C++ doesn't support flexibe arrays */ +#ifndef __cplusplus + lumiera_interfaceslot functions[]; +#endif +}; + + +#endif /* LUMIERA_INTERFACE_H */ +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ From f114b04509f333b079bf68de4c61cefbf168ecf7 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 15 Sep 2008 15:55:30 +0200 Subject: [PATCH 42/66] Documentation for the interface system and small refactorings on the fly --- src/lib/interface.h | 248 +++++++++++++++++++++++++++++++++----------- 1 file changed, 186 insertions(+), 62 deletions(-) diff --git a/src/lib/interface.h b/src/lib/interface.h index bc114f7e7..5b3cd6c5d 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -24,21 +24,118 @@ #include "lib/luid.h" #include "lib/ppmpl.h" +/* TODO better doxygen formating */ + +/** + * @file + * Lumiera interface macros and structures. + * + * Instead just simple function/library bindings, Lumiera uses a system of + * versioned interfaces. This interfaces are C-binding compatible and thus + * can be used by any language which can bind to C. This interfaces are versioned + * to provide exceptional forward and backward compatibility for both, source and + * binary deployment of modules. This interfaces play a central role on the Lumiera + * architecture, other facilities, like serializing sessions and distributed computing + * will use them extensively. + * + * Overview + * + * Interfaces are used for two purposes in Lumiera: + * 1. The core uses them internally and exports its functionality though them. + * 2. Plugins (effects,...) extend Lumiera by providing interfaces + * + * We define some macros here which ease the declaration and definition of interfaces. + * + * Declaration of an interface happens in a header and has the form: + * LUMIERA_INTERFACE_DECLARE(name, version, + * LUMIERA_INTERFACE_SLOT(ret, name, params), + * ... + * ) + * Any code which want to use this interface must then include its declaration. + * + * Basic definition of an interface is done by: + * LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, + * LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, + * LUMIERA_INTERFACE_MAP (slot, function, luid), + * ... + * ), + * ... + * ) + * + * There are 2 ways to define collections of interfaces: + * LUMIERA_EXPORT(queryfunc, + * LUMIERA_INTERFACE_DEFINE(...), + * ... + * ) + * to export interfaces from the core. + * + * LUMIERA_PLUGIN(descriptor, data, acquire, release, luid, + * LUMIERA_INTERFACE_DEFINE(...), + * ... + * ) + * is used to export interfaces from a plugin. + * + * Naming and Versioning + * Interfaces have unique names and a major and minor version. The name and the major version + * is used to construct a C identifier for the interface, the minor version is implicit defined + * by the number of functions a interface. Interface instances are not versioned by the + * interface system, versioning these shall be defined somewhere else. + * + * Slot names are normal C identifiers, how these shall be versioned has to be defined somewhere + * else and is not subject of the interface system. Each function can has its own unique uuid. + */ + + /* Interface declaration macros */ +/** + * Construct a type identifier for an interface + * @param name name of the interface + * @param version major version of this interface + */ #define LUMIERA_INTERFACE_INAME(name, version) lumiera_interface_##name##_##version -#define LUMIERA_INTERFACE_DNAME(iname, dname, version) dname##_##version##_interface +/** + * Construct a definition identifier for an interface + * @param iname name of the interface + * @param dname name for the instance + * @param version major version of the interface + */ +#define LUMIERA_INTERFACE_DNAME(iname, dname, version) lumiera_##dname##_##version##_interface + +/** + * Construct the type of the interface + * @param name name of the interface + * @param version major version of this interface + */ #define LUMIERA_INTERFACE_TYPE(name, version) struct LUMIERA_INTERFACE_INAME(name, version) + +/** + * Construct a cast to the target interface type + * Used to cast a generic LumieraInterface to the real type + * @param name name of the interface + * @param version major version of this interface + */ #define LUMIERA_INTERFACE_CAST(name, version) (LUMIERA_INTERFACE_TYPE(name, version)*) -#define PPMPL_FOREACH_LUMIERA_INTERFACE_SLOT(ret, name, params) ret (*name) params; lumiera_uid name##_uid; - - +/** + * Declare an interface. + * @param name name of the interface + * @param version major version of this interface declaration. 0 denotes a experimental interface, + * otherwise this shall be counting from 1 upwards for each new (incompatible) change of an interface. + * The older interface declarations may still be maintained in parallel (backwards compatibility!). + * @param ... Slot declarations for the functions provided by this interface @see LUMIERA_INTERFACE_SLOT + * The number of Slots in an interface defines its 'minor' version. + * New slots must be added at the end. The prototype and order of existing slots must not be changed. + * Slots may be renamed, for example a slot 'foo' can be renamed to 'foo_old' when a new 'foo' slot is + * added. Binary modules will then still use the 'foo_old' slot which was the 'foo' slot at their + * compile time while compiling modules from source will use the new 'foo' slot. This may be + * intentionally used to break compilation and force the update of modules to a new api. + */ #define LUMIERA_INTERFACE_DECLARE(name, version, ...) \ LUMIERA_INTERFACE_TYPE(name, version) \ { \ @@ -46,34 +143,34 @@ LUMIERA_INTERFACE_TYPE(name, version) \ PPMPL_FOREACH(_, __VA_ARGS__) \ } +/** + * Declare function slot inside an interface. + * @param ret return type of the function + * @param name name of this slot + * @param params parentized list of parameters for the function + */ +#define LUMIERA_INTERFACE_SLOT(ret, name, params) ret (*name) params; lumiera_uid name##_uid; +#define PPMPL_FOREACH_LUMIERA_INTERFACE_SLOT(ret, name, params) LUMIERA_INTERFACE_SLOT(ret, name, params) + /* Interface definition macros */ -#ifdef __cplusplus -#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, uid) \ - function, LUMIERA_UID_INITIALIZER (uid), -#else -#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, uid) \ - .slot = function, .slot##_uid = LUMIERA_UID_INITIALIZER (uid), -#endif - - /** - * - * - * - * - * - * - * - * - * - * + * Define a interface instance. + * @param iname name of the interface to instance + * @param version major version of the interface to instance + * @param name name of the instance + * @param descriptor pointer to an interface instance which provides a description of this interface, might be NULL + * @param data a opaque void* pointer which is passed to the implementation + * @param acquire a function which is called whenever this interface is opened for using, might be NULL + * @param release a function which is called whenever this interface is closed after use, might be NULL + * @param ... map functions to interface slots @see LUMIERA_INTERFACE_MAP */ -#define LUMIERA_INTERFACE_INITIALIZER(iname, version, name, descriptor, data, acquire, release, ...) \ +#define LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ +LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, version) = \ { \ { \ #name, \ @@ -88,50 +185,50 @@ LUMIERA_INTERFACE_TYPE(name, version) \ } -#define LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ -LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, version) = \ - LUMIERA_INTERFACE_INITIALIZER(iname, version, name, descriptor, data, acquire, release, __VA_ARGS__); +/** + * Map a function to a interface slot + * @param slot name of the slot to be mapped + * @param function name of the function to be mapped on slot + * @param luid unique identifier for this function, use the magic word LUIDGEN here and run the + * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically + * + * @note C++ requires that all mappings are in the same order than defined in the interface declaration, + * this would be good style for C too anyways + */ +#ifdef __cplusplus +#define LUMIERA_INTERFACE_MAP(slot, function, luid) \ + function, LUMIERA_UID_INITIALIZER (uid), +#else +#define LUMIERA_INTERFACE_MAP(slot, function, luid) \ + .slot = function, .slot##_uid = LUMIERA_UID_INITIALIZER (luid), +#endif +#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, luid) LUMIERA_INTERFACE_MAP(slot, function, luid) - -#define PPMPL_FOREACH_L1_P1_LUMIERA_INTERFACE(iname, version, name, descriptor, data, acquire, release, ...) \ -LUMIERA_INTERFACE_DEFINE (iname, version, \ - name, \ - descriptor, \ - data, \ - acquire, \ - release, \ - __VA_ARGS__ \ +#define PPMPL_FOREACH_L1_P1_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ +LUMIERA_INTERFACE_DEFINE (iname, version, \ + name, \ + descriptor, \ + data, \ + acquire, \ + release, \ + __VA_ARGS__ \ ); -#define PPMPL_FOREACH_L1_P2_LUMIERA_INTERFACE(iname, version, name, descriptor, data, acquire, release, ...) \ +#define PPMPL_FOREACH_L1_P2_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ &LUMIERA_INTERFACE_DNAME(iname, name, version).interface_header_, -#define LUMIERA_PLUGIN(descriptor, data, acquire, release, luid, ...) \ -PPMPL_FOREACH_L1(_P1_, __VA_ARGS__) \ -static const LumieraInterface* \ -plugin_interfaces (void) \ -{ \ - static const LumieraInterface interfaces[] = \ - { \ - PPMPL_FOREACH_L1(_P2_, __VA_ARGS__) \ - NULL \ - }; \ - return interfaces; \ -} \ -LUMIERA_INTERFACE_DEFINE (plugin, 0, \ - lumiera_plugin, \ - NULL, \ - NULL, \ - NULL, \ - NULL, \ - LUMIERA_INTERFACE_MAP (plugin_interfaces, plugin_interfaces, luid) \ - ) - - - +/** + * Generate interface container suitable for enumerating interfaces. + * This takes a list of interface definitions, instantiates them and places pointers to them + * into a zero terminated array which address is returned by the a created function. + * For interfaces generated by he core, the user is responsible to register these at the + * plugindb dynamically + * @param queryfunc name of the function to be created. + * @param ... list of LUMIERA_INTERFACE_DEFINE() for all interfaces this plugin provides. + */ #define LUMIERA_EXPORT(queryfunc, ...) \ PPMPL_FOREACH_L1(_P1_, __VA_ARGS__) \ static const LumieraInterface* \ @@ -146,6 +243,33 @@ queryfunc (void) \ } \ +/** + * Generate interface container suitable for a lumiera plugin. + * This takes a list of interface definitions and places pointers to them into a zero terminated array. Further + * it instances a local 'plugin interface' which will be picked up by the plugin loader to query the array of + * provided interfaces. + * @param descriptor pointer to an interface instance which provides a description of this plugin, might be NULL + * @param data a opaque void* pointer which is passed to the implementation + * @param acquire a function which is called whenever the plugin interface is opened for using, might be NULL + * @param release a function which is called whenever this plugin interface is closed after use, might be NULL + * @param luid unique identifier for the this plugin interfaces query, use the magic word LUIDGEN here and run the + * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically + * @param ... list of LUMIERA_INTERFACE_DEFINE() for all interfaces this plugin provides. + */ +#define LUMIERA_PLUGIN(descriptor, data, acquire, release, luid, ...) \ +LUMIERA_EXPORT(plugin_interfaces, __VA_ARGS__) \ +LUMIERA_INTERFACE_DEFINE (plugin, 0, \ + lumiera_plugin, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + LUMIERA_INTERFACE_MAP (plugin_interfaces, plugin_interfaces, luid) \ + ) + + + + typedef struct lumiera_interfaceslot_struct lumiera_interfaceslot; typedef lumiera_interfaceslot* LumieraInterfaceslot; @@ -154,7 +278,7 @@ typedef lumiera_interface* LumieraInterface; /** * This is just a placeholder for an entry in a interface table. - * It consists of one here generic, later correctly prototyped functionppointer and + * It consists of one here generic, later correctly prototyped function pointer and * a unique identifier which is associated with this function. */ struct lumiera_interfaceslot_struct @@ -187,8 +311,8 @@ struct lumiera_interface_struct /** called when finished with this interface, must match the acquire calls */ void (*release)(LumieraInterface self); - /** placeholder array for the following function slots, C++ doesn't support flexibe arrays */ #ifndef __cplusplus + /** placeholder array for the following function slots, C++ doesn't support flexible arrays */ lumiera_interfaceslot functions[]; #endif }; From c84d10c9d5663ee5e18c50caea3b415bb738a2b1 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 15 Sep 2008 17:33:43 +0200 Subject: [PATCH 43/66] Add a CALL macro to interface.h --- src/lib/interface.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib/interface.h b/src/lib/interface.h index 5b3cd6c5d..dac45ee91 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -268,6 +268,15 @@ LUMIERA_INTERFACE_DEFINE (plugin, 0, ) +/** + * Calls a function in interface + * @param name name of the interface + * @param version version of the interface + * @param handle pointer to the interface (LumieraInterface) + * @param slot name of the slot to be called + */ +#define LUMIERA_INTERFACE_CALL(name, version, handle, slot) \ + (LUMIERA_INTERFACE_CAST(name, version) handle)->slot typedef struct lumiera_interfaceslot_struct lumiera_interfaceslot; From feb9ba7db9cc9dfc813304cfbc0afc0f58f46324 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 15 Sep 2008 20:42:49 +0200 Subject: [PATCH 44/66] provide some example code for the 'interface' system * test-interfaces.c is just a mockup to show how interfaces are created * fix some bugs introduced with a futile refactoring try at the last commit --- src/lib/interface.h | 37 ++++------ tests/Makefile.am | 5 ++ tests/library/test-interfaces.c | 126 ++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 tests/library/test-interfaces.c diff --git a/src/lib/interface.h b/src/lib/interface.h index dac45ee91..5c1683bae 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -54,13 +54,10 @@ * Any code which want to use this interface must then include its declaration. * * Basic definition of an interface is done by: - * LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, - * LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, - * LUMIERA_INTERFACE_MAP (slot, function, luid), - * ... - * ), - * ... - * ) + * LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, data, acquire, release, + * LUMIERA_INTERFACE_MAP (slot, function, luid), + * ... + * ) * * There are 2 ways to define collections of interfaces: * LUMIERA_EXPORT(queryfunc, @@ -149,8 +146,7 @@ LUMIERA_INTERFACE_TYPE(name, version) \ * @param name name of this slot * @param params parentized list of parameters for the function */ -#define LUMIERA_INTERFACE_SLOT(ret, name, params) ret (*name) params; lumiera_uid name##_uid; -#define PPMPL_FOREACH_LUMIERA_INTERFACE_SLOT(ret, name, params) LUMIERA_INTERFACE_SLOT(ret, name, params) +#define PPMPL_FOREACH_LUMIERA_INTERFACE_SLOT(ret, name, params) ret (*name) params; lumiera_uid name##_uid; /* @@ -169,7 +165,7 @@ LUMIERA_INTERFACE_TYPE(name, version) \ * @param release a function which is called whenever this interface is closed after use, might be NULL * @param ... map functions to interface slots @see LUMIERA_INTERFACE_MAP */ -#define LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ +#define LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, data, acquire, release, ...) \ LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, version) = \ { \ { \ @@ -196,24 +192,23 @@ LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, vers * this would be good style for C too anyways */ #ifdef __cplusplus -#define LUMIERA_INTERFACE_MAP(slot, function, luid) \ +#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, luid) \ function, LUMIERA_UID_INITIALIZER (uid), #else -#define LUMIERA_INTERFACE_MAP(slot, function, luid) \ +#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, luid) \ .slot = function, .slot##_uid = LUMIERA_UID_INITIALIZER (luid), #endif -#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, luid) LUMIERA_INTERFACE_MAP(slot, function, luid) #define PPMPL_FOREACH_L1_P1_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ -LUMIERA_INTERFACE_DEFINE (iname, version, \ - name, \ - descriptor, \ - data, \ - acquire, \ - release, \ - __VA_ARGS__ \ - ); +LUMIERA_INTERFACE_INSTANCE (iname, version, \ + name, \ + descriptor, \ + data, \ + acquire, \ + release, \ + __VA_ARGS__ \ + ); #define PPMPL_FOREACH_L1_P2_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ diff --git a/tests/Makefile.am b/tests/Makefile.am index fa28a115f..f3ae80391 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -48,6 +48,11 @@ test_luid_SOURCES = $(tests_srcdir)/library/test-luid.c test_luid_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_luid_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm +check_PROGRAMS += test-interfaces +test_interfaces_SOURCES = $(tests_srcdir)/library/test-interfaces.c +test_interfaces_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror +test_interfaces_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm + check_PROGRAMS += test-filedescriptors test_filedescriptors_SOURCES = $(tests_srcdir)/backend/test-filedescriptors.c test_filedescriptors_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror diff --git a/tests/library/test-interfaces.c b/tests/library/test-interfaces.c new file mode 100644 index 000000000..97c9442c5 --- /dev/null +++ b/tests/library/test-interfaces.c @@ -0,0 +1,126 @@ +/* + test-interfaces.c - test interfaces declaration and implementation + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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/interface.h" +#include "tests/test.h" + +/* + define 2 example interfaces + */ + +LUMIERA_INTERFACE_DECLARE (example1, 0, + LUMIERA_INTERFACE_SLOT (void, foo1, (const char*)), + LUMIERA_INTERFACE_SLOT (void, bar1, (const char*)), +); + +LUMIERA_INTERFACE_DECLARE (example2, 0, + LUMIERA_INTERFACE_SLOT (void, foo2, (const char*)), + LUMIERA_INTERFACE_SLOT (void, bar2, (const char*)), +); + + +/* + now the functions we want to bind to them + */ + +void +testfunc (const char* message) +{ + printf ("Called as '%s'\n", message); +} + + + +/* + implementation of some example interfaces + */ + +LUMIERA_INTERFACE_INSTANCE (example1, 0, + example1_standalone_implementation, + NULL, + NULL, + NULL, + NULL, + LUMIERA_INTERFACE_MAP (foo1, testfunc, + "\066\177\042\305\165\243\236\352\164\250\357\307\211\374\123\066"), + LUMIERA_INTERFACE_MAP (bar1, testfunc, + "\162\335\262\306\101\113\106\055\342\205\300\151\262\073\257\343") + ); + + +LUMIERA_EXPORT (interfaces_defined_here, + LUMIERA_INTERFACE_DEFINE (example1, 0, + example1_implementation, + NULL, + NULL, + NULL, + NULL, + LUMIERA_INTERFACE_MAP (foo1, testfunc, + "\214\310\136\372\003\344\163\377\075\100\070\200\375\221\227\324"), + LUMIERA_INTERFACE_MAP (bar1, testfunc, + "\262\253\067\211\157\052\212\140\114\334\231\250\340\075\214\030") + ), + LUMIERA_INTERFACE_DEFINE (example2, 0, + example2_implementation, + NULL, + NULL, + NULL, + NULL, + LUMIERA_INTERFACE_MAP (foo2, testfunc, + "\110\152\002\271\363\052\324\272\373\045\132\270\277\000\271\217"), + LUMIERA_INTERFACE_MAP (bar2, testfunc, + "\376\042\027\336\355\113\132\233\350\312\170\077\377\370\356\167") + ) + ); + + +TESTS_BEGIN + +TEST ("basic") +{ + interfaces_defined_here(); +} + +TEST ("basic") +{ +} + +TEST ("basic") +{ +} + +TEST ("basic") +{ +} + +TESTS_END + + + + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ From caa5bd10a0a0358b2e007140ba1fc1830c4a4659 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 16 Sep 2008 15:21:01 +0200 Subject: [PATCH 45/66] interface update * remove the data member from the interface structure * add a psplay node used in a upcoming interface registry * add a INLINE function mapper --- src/lib/interface.h | 84 ++++++++++++++++++++++----------- tests/library/test-interfaces.c | 31 ++++++------ 2 files changed, 72 insertions(+), 43 deletions(-) diff --git a/src/lib/interface.h b/src/lib/interface.h index 5c1683bae..808dfa167 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -23,6 +23,7 @@ #include "lib/luid.h" #include "lib/ppmpl.h" +#include "lib/psplay.h" /* TODO better doxygen formating */ @@ -53,9 +54,11 @@ * ) * Any code which want to use this interface must then include its declaration. * - * Basic definition of an interface is done by: - * LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, data, acquire, release, - * LUMIERA_INTERFACE_MAP (slot, function, luid), + * Basic definition of an interface is done by mapping functions to slots or giving + * inline definitions for slot functions: + * LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, + * LUMIERA_INTERFACE_MAP (slot, luid, function), + * LUMIERA_INTERFACE_INLINE (slot, luid, ret, params, {body}), * ... * ) * @@ -66,7 +69,7 @@ * ) * to export interfaces from the core. * - * LUMIERA_PLUGIN(descriptor, data, acquire, release, luid, + * LUMIERA_PLUGIN(descriptor, acquire, release, luid, * LUMIERA_INTERFACE_DEFINE(...), * ... * ) @@ -160,58 +163,86 @@ LUMIERA_INTERFACE_TYPE(name, version) \ * @param version major version of the interface to instance * @param name name of the instance * @param descriptor pointer to an interface instance which provides a description of this interface, might be NULL - * @param data a opaque void* pointer which is passed to the implementation * @param acquire a function which is called whenever this interface is opened for using, might be NULL * @param release a function which is called whenever this interface is closed after use, might be NULL * @param ... map functions to interface slots @see LUMIERA_INTERFACE_MAP */ -#define LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, data, acquire, release, ...) \ +#define LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, ...) \ +PPMPL_FOREACH(_P1_, __VA_ARGS__) \ LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, version) = \ { \ { \ + PSPLAYNODE_INITIALIZER, \ #name, \ version, \ sizeof (LUMIERA_INTERFACE_TYPE(iname, version)), \ descriptor, \ - data, \ acquire, \ release \ }, \ - PPMPL_FOREACH(_, __VA_ARGS__) \ + PPMPL_FOREACH(_P2_, __VA_ARGS__) \ } /** * Map a function to a interface slot * @param slot name of the slot to be mapped - * @param function name of the function to be mapped on slot * @param luid unique identifier for this function, use the magic word LUIDGEN here and run the - * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically + * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically + * @param function name of the function to be mapped on slot * * @note C++ requires that all mappings are in the same order than defined in the interface declaration, * this would be good style for C too anyways */ +#define PPMPL_FOREACH_P1_LUMIERA_INTERFACE_MAP(slot, luid, function) #ifdef __cplusplus -#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, luid) \ - function, LUMIERA_UID_INITIALIZER (uid), +#define PPMPL_FOREACH_P2_LUMIERA_INTERFACE_MAP(slot, luid, function) \ + function, LUMIERA_UID_INITIALIZER (luid), #else -#define PPMPL_FOREACH_LUMIERA_INTERFACE_MAP(slot, function, luid) \ +#define PPMPL_FOREACH_P2_LUMIERA_INTERFACE_MAP(slot, luid, function) \ .slot = function, .slot##_uid = LUMIERA_UID_INITIALIZER (luid), #endif -#define PPMPL_FOREACH_L1_P1_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ -LUMIERA_INTERFACE_INSTANCE (iname, version, \ - name, \ - descriptor, \ - data, \ - acquire, \ - release, \ - __VA_ARGS__ \ +/** + * Map a inline defined function to a interface slot + * @param slot name of the slot to be mapped + * @param luid unique identifier for this function, use the magic word LUIDGEN here and run the + * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically + * @param ret return type of the inline function + * @param params parentized list of parameters given to the function + * @param ... braced function body + * + * @note C++ requires that all mappings are in the same order than defined in the interface declaration, + * this would be good style for C too anyways + */ +#define PPMPL_FOREACH_P1_LUMIERA_INTERFACE_INLINE(slot, luid, ret, params, ...) \ + static ret \ + LUMIERA_INTERFACE_INLINE_NAME(slot) params \ + __VA_ARGS__ + +#ifdef __cplusplus +#define PPMPL_FOREACH_P2_LUMIERA_INTERFACE_INLINE(slot, luid, ret, params, ...) \ + LUMIERA_INTERFACE_INLINE_NAME(slot), LUMIERA_UID_INITIALIZER (luid), +#else +#define PPMPL_FOREACH_P2_LUMIERA_INTERFACE_INLINE(slot, luid, ret, params, ...) \ + .slot = LUMIERA_INTERFACE_INLINE_NAME(slot), .slot##_uid = LUMIERA_UID_INITIALIZER (luid), +#endif + +#define LUMIERA_INTERFACE_INLINE_NAME(slot) PPMPL_CAT(lumiera_##slot##_l, __LINE__) + + +#define PPMPL_FOREACH_L1_P1_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, acquire, release, ...) \ +LUMIERA_INTERFACE_INSTANCE (iname, version, \ + name, \ + descriptor, \ + acquire, \ + release, \ + __VA_ARGS__ \ ); -#define PPMPL_FOREACH_L1_P2_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, data, acquire, release, ...) \ +#define PPMPL_FOREACH_L1_P2_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, acquire, release, ...) \ &LUMIERA_INTERFACE_DNAME(iname, name, version).interface_header_, @@ -244,21 +275,19 @@ queryfunc (void) \ * it instances a local 'plugin interface' which will be picked up by the plugin loader to query the array of * provided interfaces. * @param descriptor pointer to an interface instance which provides a description of this plugin, might be NULL - * @param data a opaque void* pointer which is passed to the implementation * @param acquire a function which is called whenever the plugin interface is opened for using, might be NULL * @param release a function which is called whenever this plugin interface is closed after use, might be NULL * @param luid unique identifier for the this plugin interfaces query, use the magic word LUIDGEN here and run the * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically * @param ... list of LUMIERA_INTERFACE_DEFINE() for all interfaces this plugin provides. */ -#define LUMIERA_PLUGIN(descriptor, data, acquire, release, luid, ...) \ +#define LUMIERA_PLUGIN(descriptor, acquire, release, luid, ...) \ LUMIERA_EXPORT(plugin_interfaces, __VA_ARGS__) \ LUMIERA_INTERFACE_DEFINE (plugin, 0, \ lumiera_plugin, \ NULL, \ NULL, \ NULL, \ - NULL, \ LUMIERA_INTERFACE_MAP (plugin_interfaces, plugin_interfaces, luid) \ ) @@ -297,6 +326,8 @@ struct lumiera_interfaceslot_struct */ struct lumiera_interface_struct { + /** all known interfaces are registered in a tree */ + psplaynode node; /** name of the interface */ const char* name; /** major version, 0 means experimental */ @@ -307,9 +338,6 @@ struct lumiera_interface_struct /** metadata descriptor, itself a interface (or NULL) */ LumieraInterface descriptor; - /** generic data to be used by the implementation */ - void* data; - /** must be called before this interface is used, might be nested */ LumieraInterface (*acquire)(LumieraInterface self); /** called when finished with this interface, must match the acquire calls */ diff --git a/tests/library/test-interfaces.c b/tests/library/test-interfaces.c index 97c9442c5..71962bf75 100644 --- a/tests/library/test-interfaces.c +++ b/tests/library/test-interfaces.c @@ -58,11 +58,14 @@ LUMIERA_INTERFACE_INSTANCE (example1, 0, NULL, NULL, NULL, - NULL, - LUMIERA_INTERFACE_MAP (foo1, testfunc, - "\066\177\042\305\165\243\236\352\164\250\357\307\211\374\123\066"), - LUMIERA_INTERFACE_MAP (bar1, testfunc, - "\162\335\262\306\101\113\106\055\342\205\300\151\262\073\257\343") + LUMIERA_INTERFACE_INLINE (foo1, "\066\177\042\305\165\243\236\352\164\250\357\307\211\374\123\066", + void, (const char* msg), + { + printf ("inline\n"); + } + ), + LUMIERA_INTERFACE_MAP (bar1, "\162\335\262\306\101\113\106\055\342\205\300\151\262\073\257\343", + testfunc) ); @@ -72,22 +75,20 @@ LUMIERA_EXPORT (interfaces_defined_here, NULL, NULL, NULL, - NULL, - LUMIERA_INTERFACE_MAP (foo1, testfunc, - "\214\310\136\372\003\344\163\377\075\100\070\200\375\221\227\324"), - LUMIERA_INTERFACE_MAP (bar1, testfunc, - "\262\253\067\211\157\052\212\140\114\334\231\250\340\075\214\030") + LUMIERA_INTERFACE_MAP (foo1, "\214\310\136\372\003\344\163\377\075\100\070\200\375\221\227\324", + testfunc), + LUMIERA_INTERFACE_MAP (bar1, "\262\253\067\211\157\052\212\140\114\334\231\250\340\075\214\030", + testfunc) ), LUMIERA_INTERFACE_DEFINE (example2, 0, example2_implementation, NULL, NULL, NULL, - NULL, - LUMIERA_INTERFACE_MAP (foo2, testfunc, - "\110\152\002\271\363\052\324\272\373\045\132\270\277\000\271\217"), - LUMIERA_INTERFACE_MAP (bar2, testfunc, - "\376\042\027\336\355\113\132\233\350\312\170\077\377\370\356\167") + LUMIERA_INTERFACE_MAP (foo2, "\110\152\002\271\363\052\324\272\373\045\132\270\277\000\271\217", + testfunc), + LUMIERA_INTERFACE_MAP (bar2, "\376\042\027\336\355\113\132\233\350\312\170\077\377\370\356\167", + testfunc) ) ); From b252807c556a5f6bcd951761e9eed5408cc6a6bd Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 16 Sep 2008 15:38:39 +0200 Subject: [PATCH 46/66] functions for interface acquire/release --- src/lib/interface.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ src/lib/interface.h | 10 +++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/lib/interface.c diff --git a/src/lib/interface.c b/src/lib/interface.c new file mode 100644 index 000000000..7972fe81e --- /dev/null +++ b/src/lib/interface.c @@ -0,0 +1,54 @@ +/* + interface.c - Lumiera interface api + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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/interface.h" + +/** + * @file + * interface api + */ + + +LumieraInterface +lumiera_interface_acquire (LumieraInterface self) +{ + if (self && self->acquire) + return self->acquire (self); + + return self; +} + +void +lumiera_interface_release (LumieraInterface self) +{ + if (self && self->release) + self->release (self); +} + + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/interface.h b/src/lib/interface.h index 808dfa167..7e35887f5 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -349,6 +349,16 @@ struct lumiera_interface_struct #endif }; +/* + API to handle interfaces + */ + +LumieraInterface +lumiera_interface_acquire (LumieraInterface self); + +void +lumiera_interface_release (LumieraInterface self); + #endif /* LUMIERA_INTERFACE_H */ /* From 66a114207994774425fbedd5ae2ce21377fbf895 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 19 Sep 2008 15:39:54 +0200 Subject: [PATCH 47/66] apply the new defined nameing rules to interface.h --- src/lib/interface.h | 59 ++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/lib/interface.h b/src/lib/interface.h index 7e35887f5..865c41a62 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -95,7 +95,7 @@ * @param name name of the interface * @param version major version of this interface */ -#define LUMIERA_INTERFACE_INAME(name, version) lumiera_interface_##name##_##version +#define LUMIERA_INTERFACE_INAME(name, version) name##_##version /** * Construct a definition identifier for an interface @@ -103,7 +103,7 @@ * @param dname name for the instance * @param version major version of the interface */ -#define LUMIERA_INTERFACE_DNAME(iname, dname, version) lumiera_##dname##_##version##_interface +#define LUMIERA_INTERFACE_DNAME(iname, dname, version) PPMPL_CAT (LUMIERA_INTERFACE_INAME(name, version), _##dname) /** * Construct the type of the interface @@ -167,20 +167,21 @@ LUMIERA_INTERFACE_TYPE(name, version) \ * @param release a function which is called whenever this interface is closed after use, might be NULL * @param ... map functions to interface slots @see LUMIERA_INTERFACE_MAP */ -#define LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, ...) \ -PPMPL_FOREACH(_P1_, __VA_ARGS__) \ -LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, version) = \ -{ \ - { \ - PSPLAYNODE_INITIALIZER, \ - #name, \ - version, \ - sizeof (LUMIERA_INTERFACE_TYPE(iname, version)), \ - descriptor, \ - acquire, \ - release \ - }, \ - PPMPL_FOREACH(_P2_, __VA_ARGS__) \ +#define LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, ...) \ +PPMPL_FOREACH(_P1_, __VA_ARGS__) \ +LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, version) = \ +{ \ +{ \ + PSPLAYNODE_INITIALIZER, \ + #iname, \ + version, \ + #name, \ + sizeof (LUMIERA_INTERFACE_TYPE(iname, version)), \ + descriptor, \ + acquire, \ + release \ +}, \ +PPMPL_FOREACH(_P2_, __VA_ARGS__) \ } @@ -283,8 +284,8 @@ queryfunc (void) \ */ #define LUMIERA_PLUGIN(descriptor, acquire, release, luid, ...) \ LUMIERA_EXPORT(plugin_interfaces, __VA_ARGS__) \ -LUMIERA_INTERFACE_DEFINE (plugin, 0, \ - lumiera_plugin, \ +LUMIERA_INTERFACE_DEFINE (lumieraorg_plugin, 0, \ + lumieraorg_plugin_0, \ NULL, \ NULL, \ NULL, \ @@ -292,15 +293,13 @@ LUMIERA_INTERFACE_DEFINE (plugin, 0, ) + /** - * Calls a function in interface - * @param name name of the interface - * @param version version of the interface - * @param handle pointer to the interface (LumieraInterface) - * @param slot name of the slot to be called + * create a handle for a interface (WIP) */ -#define LUMIERA_INTERFACE_CALL(name, version, handle, slot) \ - (LUMIERA_INTERFACE_CAST(name, version) handle)->slot +#define LUMIERA_INTERFACE_HANDLE(interface, version, reserved, name, handle) \ + LUMIERA_INTERFACE_TYPE(interface, version)* handle = LUMIERA_INTERFACE_CAST(interface, 0) \ + lumiera_interfaceregistry_interface_find (#interface, version, #name); typedef struct lumiera_interfaceslot_struct lumiera_interfaceslot; @@ -328,10 +327,16 @@ struct lumiera_interface_struct { /** all known interfaces are registered in a tree */ psplaynode node; - /** name of the interface */ - const char* name; + + /** name of the interface (type) */ + const char* interface; + /** major version, 0 means experimental */ unsigned version; + + /** name of this instance */ + const char* name; + /** size of the whole interface structure (minor version) */ size_t size; From 4bfe430f38ccfcfd40984ad5b8a665782104eb59 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 19 Sep 2008 15:45:24 +0200 Subject: [PATCH 48/66] Interfaceregistry for 'active' interfaces All interfaces which are available get registered in a tree. This will be used internal for lookup interfaces. The higher level calls will be an amalgamation of this lookup and the upcoming plugindb and loader. --- src/backend/Makefile.am | 2 ++ src/backend/interfaceregistry.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 2cde41ae2..c452814db 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -28,6 +28,7 @@ liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/filehandle.c \ $(liblumibackend_a_srcdir)/filedescriptor.c \ $(liblumibackend_a_srcdir)/filehandlecache.c \ + $(liblumibackend_a_srcdir)/interfaceregistry.c \ $(liblumibackend_a_srcdir)/config.c \ $(liblumibackend_a_srcdir)/config_typed.c \ $(liblumibackend_a_srcdir)/config_wordlist.c \ @@ -44,6 +45,7 @@ noinst_HEADERS += \ $(liblumibackend_a_srcdir)/filehandle.h \ $(liblumibackend_a_srcdir)/filedescriptor.h \ $(liblumibackend_a_srcdir)/filehandlecache.h \ + $(liblumibackend_a_srcdir)/interfaceregistry.h \ $(liblumibackend_a_srcdir)/config.h \ $(liblumibackend_a_srcdir)/configentry.h \ $(liblumibackend_a_srcdir)/configitem.h \ diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c index 89d26fe60..d3f070b56 100644 --- a/src/backend/interfaceregistry.c +++ b/src/backend/interfaceregistry.c @@ -42,7 +42,6 @@ NOBUG_DEFINE_FLAG_PARENT (interfaceregistry, backend); static PSplay interfaceregistry; lumiera_mutex lumiera_interface_mutex; - static int cmp_fn (const void* keya, const void* keyb); From 0342e19c128ff18adac78d0880f1a8e5fdeea814 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 19 Sep 2008 15:47:55 +0200 Subject: [PATCH 49/66] move the interfaces test to backend, because its going to be stateful --- tests/Makefile.am | 4 +- tests/{library => backend}/test-interfaces.c | 44 ++++++++++++++++---- 2 files changed, 37 insertions(+), 11 deletions(-) rename tests/{library => backend}/test-interfaces.c (71%) diff --git a/tests/Makefile.am b/tests/Makefile.am index f3ae80391..0c7f80f12 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -49,9 +49,9 @@ test_luid_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_luid_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm check_PROGRAMS += test-interfaces -test_interfaces_SOURCES = $(tests_srcdir)/library/test-interfaces.c +test_interfaces_SOURCES = $(tests_srcdir)/backend/test-interfaces.c test_interfaces_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -test_interfaces_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm +test_interfaces_LDADD = liblumibackend.a liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm check_PROGRAMS += test-filedescriptors test_filedescriptors_SOURCES = $(tests_srcdir)/backend/test-filedescriptors.c diff --git a/tests/library/test-interfaces.c b/tests/backend/test-interfaces.c similarity index 71% rename from tests/library/test-interfaces.c rename to tests/backend/test-interfaces.c index 71962bf75..fb1c4d30a 100644 --- a/tests/library/test-interfaces.c +++ b/tests/backend/test-interfaces.c @@ -20,18 +20,20 @@ */ #include "lib/interface.h" +#include "backend/interfaceregistry.h" +//#include "lib/interfacedescriptor.h" #include "tests/test.h" /* define 2 example interfaces */ -LUMIERA_INTERFACE_DECLARE (example1, 0, +LUMIERA_INTERFACE_DECLARE (lumieraorg_testexample_one, 0, LUMIERA_INTERFACE_SLOT (void, foo1, (const char*)), LUMIERA_INTERFACE_SLOT (void, bar1, (const char*)), ); -LUMIERA_INTERFACE_DECLARE (example2, 0, +LUMIERA_INTERFACE_DECLARE (lumieraorg_testexample_two, 0, LUMIERA_INTERFACE_SLOT (void, foo2, (const char*)), LUMIERA_INTERFACE_SLOT (void, bar2, (const char*)), ); @@ -53,8 +55,8 @@ testfunc (const char* message) implementation of some example interfaces */ -LUMIERA_INTERFACE_INSTANCE (example1, 0, - example1_standalone_implementation, +LUMIERA_INTERFACE_INSTANCE (lumieraorg_testexample_one, 0, + lumieraorg_standalone_test, NULL, NULL, NULL, @@ -70,8 +72,8 @@ LUMIERA_INTERFACE_INSTANCE (example1, 0, LUMIERA_EXPORT (interfaces_defined_here, - LUMIERA_INTERFACE_DEFINE (example1, 0, - example1_implementation, + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_one, 0, + lumieraorg_first_test, NULL, NULL, NULL, @@ -80,8 +82,8 @@ LUMIERA_EXPORT (interfaces_defined_here, LUMIERA_INTERFACE_MAP (bar1, "\262\253\067\211\157\052\212\140\114\334\231\250\340\075\214\030", testfunc) ), - LUMIERA_INTERFACE_DEFINE (example2, 0, - example2_implementation, + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_two, 0, + lumieraorg_second_test, NULL, NULL, NULL, @@ -97,7 +99,31 @@ TESTS_BEGIN TEST ("basic") { - interfaces_defined_here(); + lumiera_interfaceregistry_init (); + + lumiera_interfaceregistry_bulkregister_interfaces (interfaces_defined_here()); + + + /* some ugly lowlevel handling tests */ + + LumieraInterface handle1 = + lumiera_interfaceregistry_interface_find ("lumieraorg_testexample_one", 0, "lumieraorg_first_test"); + + (LUMIERA_INTERFACE_CAST(lumieraorg_testexample_one, 0)handle1)->bar1 ("this is bar1"); + + + LUMIERA_INTERFACE_TYPE(lumieraorg_testexample_two, 0)* handle2 = LUMIERA_INTERFACE_CAST(lumieraorg_testexample_two, 0) + lumiera_interfaceregistry_interface_find ("lumieraorg_testexample_two", 0, "lumieraorg_second_test"); + + handle2->foo2 ("this is foo2"); + + /* higher level, WIP */ + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_two, 0, 0, lumieraorg_second_test, handle3); + handle3->bar2 ("this is bar2"); + + lumiera_interfaceregistry_bulkremove_interfaces (interfaces_defined_here()); + lumiera_interfaceregistry_destroy (); } TEST ("basic") From bb9c53d32d4cc9ae932b426986286cbf32df3963 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sun, 21 Sep 2008 23:52:14 +0200 Subject: [PATCH 50/66] change parameter order for _DNAME in interface.h, add a _REF macro --- src/lib/interface.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/lib/interface.h b/src/lib/interface.h index 865c41a62..836ac934c 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -100,10 +100,21 @@ /** * Construct a definition identifier for an interface * @param iname name of the interface - * @param dname name for the instance * @param version major version of the interface + * @param dname name for the instance */ -#define LUMIERA_INTERFACE_DNAME(iname, dname, version) PPMPL_CAT (LUMIERA_INTERFACE_INAME(name, version), _##dname) +#define LUMIERA_INTERFACE_DNAME(iname, version, dname) PPMPL_CAT (LUMIERA_INTERFACE_INAME(iname, version), _##dname) + + +/** + * Return a reference (pointer) to an interface implementation + * @param iname name of the interface + * @param version major version of the interface + * @param dname name for the instance + */ +#define LUMIERA_INTERFACE_REF(iname, version, dname) \ + (LumieraInterface)&LUMIERA_INTERFACE_DNAME(iname, version, dname) + /** * Construct the type of the interface @@ -169,7 +180,7 @@ LUMIERA_INTERFACE_TYPE(name, version) \ */ #define LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, ...) \ PPMPL_FOREACH(_P1_, __VA_ARGS__) \ -LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, name, version) = \ +LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, version, name) = \ { \ { \ PSPLAYNODE_INITIALIZER, \ @@ -244,7 +255,7 @@ LUMIERA_INTERFACE_INSTANCE (iname, version, #define PPMPL_FOREACH_L1_P2_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, acquire, release, ...) \ - &LUMIERA_INTERFACE_DNAME(iname, name, version).interface_header_, + &LUMIERA_INTERFACE_DNAME(iname, version, name).interface_header_, /** From c9ac9564176a57a1773d8111be7eebcdbe58ec08 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sun, 21 Sep 2008 23:52:32 +0200 Subject: [PATCH 51/66] cosmetics --- src/lib/interface.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/interface.h b/src/lib/interface.h index 836ac934c..75eb2bb10 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -267,18 +267,18 @@ LUMIERA_INTERFACE_INSTANCE (iname, version, * @param queryfunc name of the function to be created. * @param ... list of LUMIERA_INTERFACE_DEFINE() for all interfaces this plugin provides. */ -#define LUMIERA_EXPORT(queryfunc, ...) \ -PPMPL_FOREACH_L1(_P1_, __VA_ARGS__) \ -static const LumieraInterface* \ -queryfunc (void) \ -{ \ - static const LumieraInterface interfaces[] = \ - { \ - PPMPL_FOREACH_L1(_P2_, __VA_ARGS__) \ - NULL \ - }; \ - return interfaces; \ -} \ +#define LUMIERA_EXPORT(queryfunc, ...) \ +PPMPL_FOREACH_L1(_P1_, __VA_ARGS__) \ +static LumieraInterface* \ +queryfunc (void) \ +{ \ + static LumieraInterface interfaces[] = \ + { \ + PPMPL_FOREACH_L1(_P2_, __VA_ARGS__) \ + NULL \ + }; \ + return interfaces; \ +} /** From 4414af375127c52a620d6d3a5328d9b238a338d7 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 22 Sep 2008 00:00:43 +0200 Subject: [PATCH 52/66] add a 'weak' flag for interface_acquire/release() When interfaces cross depend on each other nested acquiring from their acquire handlers would introduce cyclic references which cant be cleaned easily. By flagging such nested acquisitions as weak the respective handler functions can account for this. Example: maintain 2 ref counters one for strong and one for weak acquisitions. At release time all acquired nested handles get closed when the strong counter drops to zero, which causes that cross reference releases will eventually drop the weak count to zero too. Then all resources can be freed and the interface is properly shut down. --- src/lib/interface.c | 8 ++++---- src/lib/interface.h | 23 +++++++++++++++++------ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/lib/interface.c b/src/lib/interface.c index 7972fe81e..0a566534d 100644 --- a/src/lib/interface.c +++ b/src/lib/interface.c @@ -28,19 +28,19 @@ LumieraInterface -lumiera_interface_acquire (LumieraInterface self) +lumiera_interface_acquire (LumieraInterface self, int weak) { if (self && self->acquire) - return self->acquire (self); + return self->acquire (self, weak); return self; } void -lumiera_interface_release (LumieraInterface self) +lumiera_interface_release (LumieraInterface self, int weak) { if (self && self->release) - self->release (self); + self->release (self, weak); } diff --git a/src/lib/interface.h b/src/lib/interface.h index 75eb2bb10..6713fc428 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -354,10 +354,21 @@ struct lumiera_interface_struct /** metadata descriptor, itself a interface (or NULL) */ LumieraInterface descriptor; - /** must be called before this interface is used, might be nested */ - LumieraInterface (*acquire)(LumieraInterface self); - /** called when finished with this interface, must match the acquire calls */ - void (*release)(LumieraInterface self); + /** + * Must be called before this interface is used. + * might be nested. + * @param self pointer to the interface to be acquired + * @param weak indicates a weak aquisition when this is not null. Used to resolve aquisition for cross dependencies + * @return pointer to the interface or NULL on error + */ + LumieraInterface (*acquire)(LumieraInterface self, int weak); + /** + * called when finished using this interface + * must match the acquire calls + * @param self pointer to the interface to be released + * @param weak indicates a weak release, must be the same value as used for the acquire function + */ + void (*release)(LumieraInterface self, int weak); #ifndef __cplusplus /** placeholder array for the following function slots, C++ doesn't support flexible arrays */ @@ -370,10 +381,10 @@ struct lumiera_interface_struct */ LumieraInterface -lumiera_interface_acquire (LumieraInterface self); +lumiera_interface_acquire (LumieraInterface self, int weak); void -lumiera_interface_release (LumieraInterface self); +lumiera_interface_release (LumieraInterface self, int weak); #endif /* LUMIERA_INTERFACE_H */ From a6ddb55e9b352ae3c0027c218d2cdcb1f2d8a0d4 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sun, 28 Sep 2008 03:49:28 +0200 Subject: [PATCH 53/66] use a recursive mutex to lock interface operations locking interfaceregistry operations and later on open/close etc --- src/backend/interfaceregistry.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c index d3f070b56..89d26fe60 100644 --- a/src/backend/interfaceregistry.c +++ b/src/backend/interfaceregistry.c @@ -42,6 +42,7 @@ NOBUG_DEFINE_FLAG_PARENT (interfaceregistry, backend); static PSplay interfaceregistry; lumiera_mutex lumiera_interface_mutex; + static int cmp_fn (const void* keya, const void* keyb); From 405a578c42c3dcc1d707e389ac981ed76087597b Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 13 Oct 2008 21:56:23 +0200 Subject: [PATCH 54/66] WIP: interface open/close handling cross dependencies --- src/backend/Makefile.am | 3 +- src/backend/interface.c | 281 ++++++++++++++++++++++++++++++++ src/backend/interfaceregistry.c | 16 +- src/backend/interfaceregistry.h | 2 + src/lib/Makefile.am | 1 - src/lib/interface.c | 54 ------ src/lib/interface.h | 60 +++++-- 7 files changed, 348 insertions(+), 69 deletions(-) create mode 100644 src/backend/interface.c delete mode 100644 src/lib/interface.c diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index c452814db..78ffea592 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -18,7 +18,7 @@ liblumibackend_a_srcdir = $(top_srcdir)/src/backend noinst_LIBRARIES += liblumibackend.a -liblumibackend_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Werror +liblumibackend_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wextra -Wall -Werror liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/mediaaccessfacade.cpp \ @@ -28,6 +28,7 @@ liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/filehandle.c \ $(liblumibackend_a_srcdir)/filedescriptor.c \ $(liblumibackend_a_srcdir)/filehandlecache.c \ + $(liblumibackend_a_srcdir)/interface.c \ $(liblumibackend_a_srcdir)/interfaceregistry.c \ $(liblumibackend_a_srcdir)/config.c \ $(liblumibackend_a_srcdir)/config_typed.c \ diff --git a/src/backend/interface.c b/src/backend/interface.c new file mode 100644 index 000000000..a93088213 --- /dev/null +++ b/src/backend/interface.c @@ -0,0 +1,281 @@ +/* + interface.c - Lumiera interface api + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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/mutex.h" +#include "lib/safeclib.h" +#include "lib/interface.h" + +#include "backend/interfaceregistry.h" + +#include + +/** + * @file + * From a programmers perspective interfaces only need to be opened when needed and closed + * when finished with them. There is no difference if the interface is internally provided + * by the core or provided by an external plugin. + * Interfaces can be opened multiple times and cross reference each other. + */ + + +extern LumieraInterface lumiera_interface_stack; + + +LumieraInterface +lumiera_interface_open (const char* interface, unsigned version, size_t minminorversion, const char* name) +{ + LumieraInterface self = NULL; + TRACE (interface, "%s", name); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + self = lumiera_interfaceregistry_interface_find (interface, version, name); + + if (!self) + { + UNIMPLEMENTED ("query plugindb and load plugin if exists"); + } + + if (self) + { + if (minminorversion > self->size) + { + UNIMPLEMENTED ("set error"); + self = NULL; + } + else + { + self = lumiera_interface_open_interface (self); + } + } + } + return self; +} + + +static void +push_dependency (LumieraInterface parent, LumieraInterface child) +{ + /* push a dependency on the dependency array, allcoate or resize it on demand */ + TRACE (interface, "%s %s", parent->name, child->name); + + /* no dependencies recorded yet, alloc a first block for 4 pointers */ + if (!parent->ndeps) + parent->deps = lumiera_calloc (parent->ndeps = 4, sizeof (LumieraInterface)); + + size_t sz = parent->ndeps; + LumieraInterface* itr = parent->deps; + + while (*itr) + { + --sz; + ++itr; + if (sz == 1) + { + /* block to small, realloc it with twice its size, we keep the block NULL terminated */ + sz = parent->ndeps + 1; + parent->ndeps *= 2; + parent->deps = lumiera_realloc (parent->deps, parent->ndeps * sizeof (LumieraInterface)); + itr = parent->deps + sz - 2; + memset (itr, 0, sz * sizeof (LumieraInterface)); + } + } + + TODO ("free the deps when unregistering the interface"); + + /* found free element, store self in dependencies */ + *itr = child; +} + + +static void +depwalk (LumieraInterface self, LumieraInterface* stack) +{ + /* increment refcount for all non-cyclic dependencies recursively */ + if (self->deps) + { + TRACE (interface, "%s %d", self->name, self->refcnt); + for (LumieraInterface* dep = self->deps; *dep; ++dep) + { + TRACE (interface, "loop %s", (*dep)->name); + int cycle = 0; + for (LumieraInterface itr = *stack; itr; itr = itr->lnk) + { + if (itr == *dep) + { + TRACE (interface, "CYCLE"); + cycle = 1; + break; + } + } + + if (!cycle) + { + ++(*dep)->refcnt; + + (*dep)->lnk = *stack; + *stack = *dep; + + depwalk (*dep, stack); + + *stack = (*dep)->lnk; + (*dep)->lnk = NULL; + } + } + } +} + + +LumieraInterface +lumiera_interface_open_interface (LumieraInterface self) +{ + static unsigned collect_dependencies = 0; + static LumieraInterface stack = NULL; + + /* + Ok, this got little more complicated than it should be, + but finally it handles any kind of cross dependencies between interfaces gracefully + */ + + if (self) + { + TRACE (interface, "%s %d (%s)", self->name, self->refcnt, stack?stack->name:""); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + /* discover cycles, cycles don't refcount! */ + int cycle = 0; + + for (LumieraInterface itr = stack; itr; itr = itr->lnk) + { + if (itr == self) + { + TRACE (interface, "CYCLE"); + cycle = 1; + break; + } + } + + /* 'stack' is ensured to be !NULL here because only a parent call can switch collect_dependencies on */ + if (collect_dependencies) + push_dependency (stack, self); + + if (!cycle) + { + ++self->refcnt; + self->lnk = stack; + stack = self; + int collect_dependencies_bak = collect_dependencies; + + if (self->refcnt == 1) + { + /* first opening, run acquire, recursive opening shall record its dependencies here */ + if (self->acquire) + { + TRACE (interface, "Acquire %s", self->name); + collect_dependencies = self->deps?0:1; + self = self->acquire (self); + } + } + else + { + /* opening again recurse dependencies */ + collect_dependencies = 0; + depwalk (self, &stack); + } + + collect_dependencies = collect_dependencies_bak; + stack = self->lnk; + self->lnk = NULL; + } + } + } + + return self; +} + + +void +lumiera_interface_close (LumieraInterface self) +{ + static LumieraInterface stack = NULL; + + if (!self) + return; + + TRACE (interface, "%s %d (%s)", self->name, self->refcnt, stack?stack->name:""); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + REQUIRE (self->refcnt); + + int cycle = 0; + + for (LumieraInterface itr = stack; itr; itr = itr->lnk) + { + if (itr == self) + { + TRACE (interface, "CYCLE"); + cycle = 1; + break; + } + } + + if (!cycle) + { + self->lnk = stack; + stack = self; + + if (self->refcnt == 1) + { + if (self->release) + { + TRACE (interface, "Release %s", self->name); + self->release (self); + } + } + else + { + if (self->deps) + { + TRACE (interface, "Recurse %s %d", self->name, self->refcnt); + + for (LumieraInterface* dep = self->deps; *dep; ++dep) + lumiera_interface_close (*dep); + } + } + + stack = self->lnk; + self->lnk = NULL; + --self->refcnt; + } + } +} + + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c index 89d26fe60..799f84b70 100644 --- a/src/backend/interfaceregistry.c +++ b/src/backend/interfaceregistry.c @@ -37,7 +37,9 @@ * by their name and major version. */ -NOBUG_DEFINE_FLAG_PARENT (interfaceregistry, backend); +NOBUG_DEFINE_FLAG_PARENT (interface_all, backend); +NOBUG_DEFINE_FLAG_PARENT (interfaceregistry, interface_all); +NOBUG_DEFINE_FLAG_PARENT (interface, interface_all); static PSplay interfaceregistry; lumiera_mutex lumiera_interface_mutex; @@ -57,10 +59,14 @@ key_fn (const PSplaynode node); void lumiera_interfaceregistry_init (void) { + NOBUG_INIT_FLAG (interface_all); NOBUG_INIT_FLAG (interfaceregistry); + NOBUG_INIT_FLAG (interface); TRACE (interfaceregistry); REQUIRE (!interfaceregistry); + TODO ("introduce a registrynode structure, place all dynamic interface stuff there, make interface_struct const"); + interfaceregistry = psplay_new (cmp_fn, key_fn, NULL); if (!interfaceregistry) LUMIERA_DIE (ERRNO); @@ -106,6 +112,7 @@ lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self) { while (*self) { + TRACE (interfaceregistry, "interface %s, version %d, instance %s", (*self)->interface, (*self)->version, (*self)->name); psplay_insert (interfaceregistry, &(*self)->node, 100); ++self; } @@ -121,7 +128,10 @@ lumiera_interfaceregistry_remove_interface (LumieraInterface self) LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) { + REQUIRE (self->refcnt == 0); psplay_remove (interfaceregistry, &self->node); + + FIXME ("free deps"); } } @@ -136,8 +146,12 @@ lumiera_interfaceregistry_bulkremove_interfaces (LumieraInterface* self) { while (*self) { + TRACE (interfaceregistry, "interface %s, version %d, instance %s", (*self)->interface, (*self)->version, (*self)->name); + REQUIRE ((*self)->refcnt == 0, "but is %d", (*self)->refcnt); + psplay_remove (interfaceregistry, &(*self)->node); ++self; + FIXME ("free deps"); } } } diff --git a/src/backend/interfaceregistry.h b/src/backend/interfaceregistry.h index 20f765c90..c6bf4a2e5 100644 --- a/src/backend/interfaceregistry.h +++ b/src/backend/interfaceregistry.h @@ -34,7 +34,9 @@ * by their name and major version. */ +NOBUG_DECLARE_FLAG (interface_all); NOBUG_DECLARE_FLAG (interfaceregistry); +NOBUG_DECLARE_FLAG (interface); extern lumiera_mutex lumiera_interface_mutex; diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 0b42926ad..2ad293158 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -31,7 +31,6 @@ liblumi_a_SOURCES = \ $(liblumi_a_srcdir)/psplay.c \ $(liblumi_a_srcdir)/mrucache.c \ $(liblumi_a_srcdir)/time.c \ - $(liblumi_a_srcdir)/interface.c \ $(liblumi_a_srcdir)/interfacedescriptor.c \ $(liblumi_a_srcdir)/appconfig.cpp diff --git a/src/lib/interface.c b/src/lib/interface.c deleted file mode 100644 index 0a566534d..000000000 --- a/src/lib/interface.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - interface.c - Lumiera interface api - - Copyright (C) Lumiera.org - 2008, Christian Thaeter - - 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/interface.h" - -/** - * @file - * interface api - */ - - -LumieraInterface -lumiera_interface_acquire (LumieraInterface self, int weak) -{ - if (self && self->acquire) - return self->acquire (self, weak); - - return self; -} - -void -lumiera_interface_release (LumieraInterface self, int weak) -{ - if (self && self->release) - self->release (self, weak); -} - - - -/* -// Local Variables: -// mode: C -// c-file-style: "gnu" -// indent-tabs-mode: nil -// End: -*/ diff --git a/src/lib/interface.h b/src/lib/interface.h index 6713fc428..16319c244 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -123,6 +123,7 @@ */ #define LUMIERA_INTERFACE_TYPE(name, version) struct LUMIERA_INTERFACE_INAME(name, version) + /** * Construct a cast to the target interface type * Used to cast a generic LumieraInterface to the real type @@ -132,7 +133,6 @@ #define LUMIERA_INTERFACE_CAST(name, version) (LUMIERA_INTERFACE_TYPE(name, version)*) - /** * Declare an interface. * @param name name of the interface @@ -167,7 +167,6 @@ LUMIERA_INTERFACE_TYPE(name, version) \ Interface definition macros */ - /** * Define a interface instance. * @param iname name of the interface to instance @@ -188,6 +187,10 @@ LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, version, n version, \ #name, \ sizeof (LUMIERA_INTERFACE_TYPE(iname, version)), \ + 0, \ + NULL, \ + 0, \ + NULL, \ descriptor, \ acquire, \ release \ @@ -308,9 +311,12 @@ LUMIERA_INTERFACE_DEFINE (lumieraorg_plugin, 0, /** * create a handle for a interface (WIP) */ -#define LUMIERA_INTERFACE_HANDLE(interface, version, reserved, name, handle) \ - LUMIERA_INTERFACE_TYPE(interface, version)* handle = LUMIERA_INTERFACE_CAST(interface, 0) \ - lumiera_interfaceregistry_interface_find (#interface, version, #name); + +#define LUMIERA_INTERFACE_HANDLE(interface, version) \ + LUMIERA_INTERFACE_TYPE(interface, version)* + +#define LUMIERA_INTERFACE_OPEN(interface, version, minminor, name) \ + LUMIERA_INTERFACE_CAST(interface, version) lumiera_interface_open (#interface, version, minminor, #name) typedef struct lumiera_interfaceslot_struct lumiera_interfaceslot; @@ -351,6 +357,12 @@ struct lumiera_interface_struct /** size of the whole interface structure (minor version) */ size_t size; + /** reference counters and link used for internal reference management */ + unsigned refcnt; + LumieraInterface lnk; + size_t ndeps; + LumieraInterface* deps; + /** metadata descriptor, itself a interface (or NULL) */ LumieraInterface descriptor; @@ -358,17 +370,15 @@ struct lumiera_interface_struct * Must be called before this interface is used. * might be nested. * @param self pointer to the interface to be acquired - * @param weak indicates a weak aquisition when this is not null. Used to resolve aquisition for cross dependencies * @return pointer to the interface or NULL on error */ - LumieraInterface (*acquire)(LumieraInterface self, int weak); + LumieraInterface (*acquire)(LumieraInterface self); /** * called when finished using this interface * must match the acquire calls * @param self pointer to the interface to be released - * @param weak indicates a weak release, must be the same value as used for the acquire function */ - void (*release)(LumieraInterface self, int weak); + void (*release)(LumieraInterface self); #ifndef __cplusplus /** placeholder array for the following function slots, C++ doesn't support flexible arrays */ @@ -380,11 +390,37 @@ struct lumiera_interface_struct API to handle interfaces */ -LumieraInterface -lumiera_interface_acquire (LumieraInterface self, int weak); +/** + * Open an interface by handle. + * This is faster because it needs no lookup, one must be sure to have a valid handle + * which mets the requirements (version) + * @param self pointer to the interface to be opened + * @return self on success or NULL on error + * @internal This function is mostly for internal purposes + */ +LumieraInterface +lumiera_interface_open_interface (LumieraInterface self); + +/** + * Open an interface by version and name. + * Looks up the requested interface, possibly loading it from a plugin. + * @param interface name of the interface definition + * @param version major version of the interface definition + * @param minminorversion required minor version (structure size) + * @param name name of the interface implementation + * @return the queried interface handle on success, else NULL + */ +LumieraInterface +lumiera_interface_open (const char* interface, unsigned version, size_t minminorversion, const char* name); + +/** + * Close an interface after use. + * @param self interface to be closed + * consider 'self' to be invalidated after this call + */ void -lumiera_interface_release (LumieraInterface self, int weak); +lumiera_interface_close (LumieraInterface iface); #endif /* LUMIERA_INTERFACE_H */ From f05f6772f12c3cdfa38096da08084a01f986d7b5 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 14 Oct 2008 23:06:43 +0200 Subject: [PATCH 55/66] Interface system refactoring * lumiera_interface are now static structures, never wrritten * introduced a lumiera_interfacenode which manages the dynamic data of interfaces. The interfaceregistry now holds this nodes. --- src/backend/interface.c | 159 +++++++++++++++++--------------- src/backend/interfaceregistry.c | 84 ++++++++++++----- src/backend/interfaceregistry.h | 32 +++++++ src/lib/interface.h | 25 ----- 4 files changed, 177 insertions(+), 123 deletions(-) diff --git a/src/backend/interface.c b/src/backend/interface.c index a93088213..0150e403e 100644 --- a/src/backend/interface.c +++ b/src/backend/interface.c @@ -36,18 +36,22 @@ */ -extern LumieraInterface lumiera_interface_stack; +static LumieraInterfacenode +lumiera_interface_open_interfacenode (LumieraInterfacenode self); + +static void +lumiera_interfacenode_close (LumieraInterfacenode self); LumieraInterface lumiera_interface_open (const char* interface, unsigned version, size_t minminorversion, const char* name) { - LumieraInterface self = NULL; + LumieraInterfacenode self = NULL; TRACE (interface, "%s", name); LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) { - self = lumiera_interfaceregistry_interface_find (interface, version, name); + self = lumiera_interfaceregistry_interfacenode_find (interface, version, name); if (!self) { @@ -56,33 +60,33 @@ lumiera_interface_open (const char* interface, unsigned version, size_t minminor if (self) { - if (minminorversion > self->size) + if (minminorversion > self->interface->size) { UNIMPLEMENTED ("set error"); self = NULL; } else { - self = lumiera_interface_open_interface (self); + self = lumiera_interface_open_interfacenode (self); } } } - return self; + return self->interface; } static void -push_dependency (LumieraInterface parent, LumieraInterface child) +push_dependency (LumieraInterfacenode parent, LumieraInterfacenode child) { /* push a dependency on the dependency array, allcoate or resize it on demand */ - TRACE (interface, "%s %s", parent->name, child->name); + TRACE (interface, "%s %s", parent->interface->name, child->interface->name); /* no dependencies recorded yet, alloc a first block for 4 pointers */ - if (!parent->ndeps) - parent->deps = lumiera_calloc (parent->ndeps = 4, sizeof (LumieraInterface)); + if (!parent->deps_size) + parent->deps = lumiera_calloc (parent->deps_size = 4, sizeof (LumieraInterfacenode)); - size_t sz = parent->ndeps; - LumieraInterface* itr = parent->deps; + size_t sz = parent->deps_size; + LumieraInterfacenode* itr = parent->deps; while (*itr) { @@ -91,33 +95,31 @@ push_dependency (LumieraInterface parent, LumieraInterface child) if (sz == 1) { /* block to small, realloc it with twice its size, we keep the block NULL terminated */ - sz = parent->ndeps + 1; - parent->ndeps *= 2; - parent->deps = lumiera_realloc (parent->deps, parent->ndeps * sizeof (LumieraInterface)); + sz = parent->deps_size + 1; + parent->deps_size *= 2; + parent->deps = lumiera_realloc (parent->deps, parent->deps_size * sizeof (LumieraInterface)); itr = parent->deps + sz - 2; memset (itr, 0, sz * sizeof (LumieraInterface)); } } - TODO ("free the deps when unregistering the interface"); - /* found free element, store self in dependencies */ *itr = child; } static void -depwalk (LumieraInterface self, LumieraInterface* stack) +depwalk (LumieraInterfacenode self, LumieraInterfacenode* stack) { /* increment refcount for all non-cyclic dependencies recursively */ if (self->deps) { - TRACE (interface, "%s %d", self->name, self->refcnt); - for (LumieraInterface* dep = self->deps; *dep; ++dep) + TRACE (interface, "%s %d", self->interface->name, self->refcnt); + for (LumieraInterfacenode* dep = self->deps; *dep; ++dep) { - TRACE (interface, "loop %s", (*dep)->name); + TRACE (interface, "loop %s", (*dep)->interface->name); int cycle = 0; - for (LumieraInterface itr = *stack; itr; itr = itr->lnk) + for (LumieraInterfacenode itr = *stack; itr; itr = itr->lnk) { if (itr == *dep) { @@ -144,11 +146,11 @@ depwalk (LumieraInterface self, LumieraInterface* stack) } -LumieraInterface -lumiera_interface_open_interface (LumieraInterface self) +static LumieraInterfacenode +lumiera_interface_open_interfacenode (LumieraInterfacenode self) { static unsigned collect_dependencies = 0; - static LumieraInterface stack = NULL; + static LumieraInterfacenode stack = NULL; /* Ok, this got little more complicated than it should be, @@ -157,14 +159,14 @@ lumiera_interface_open_interface (LumieraInterface self) if (self) { - TRACE (interface, "%s %d (%s)", self->name, self->refcnt, stack?stack->name:""); + TRACE (interface, "%s %d (%s)", self->interface->name, self->refcnt, stack?stack->interface->name:""); LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) { /* discover cycles, cycles don't refcount! */ int cycle = 0; - for (LumieraInterface itr = stack; itr; itr = itr->lnk) + for (LumieraInterfacenode itr = stack; itr; itr = itr->lnk) { if (itr == self) { @@ -188,11 +190,11 @@ lumiera_interface_open_interface (LumieraInterface self) if (self->refcnt == 1) { /* first opening, run acquire, recursive opening shall record its dependencies here */ - if (self->acquire) + if (self->interface->acquire) { - TRACE (interface, "Acquire %s", self->name); + TRACE (interface, "Acquire %s", self->interface->name); collect_dependencies = self->deps?0:1; - self = self->acquire (self); + self->interface = self->interface->acquire (self->interface); } } else @@ -216,60 +218,69 @@ lumiera_interface_open_interface (LumieraInterface self) void lumiera_interface_close (LumieraInterface self) { - static LumieraInterface stack = NULL; + TRACE (interface); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + lumiera_interfacenode_close ((LumieraInterfacenode)psplay_find (lumiera_interfaceregistry, self, 100)); + } +} + + +/* internal function, does no locking! */ +static void +lumiera_interfacenode_close (LumieraInterfacenode self) +{ + static LumieraInterfacenode stack = NULL; if (!self) return; - TRACE (interface, "%s %d (%s)", self->name, self->refcnt, stack?stack->name:""); + TRACE (interface, "%s %d (%s)", self->interface->name, self->refcnt, stack?stack->interface->name:""); - LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + REQUIRE (self->refcnt); + + int cycle = 0; + + for (LumieraInterfacenode itr = stack; itr; itr = itr->lnk) { - REQUIRE (self->refcnt); - - int cycle = 0; - - for (LumieraInterface itr = stack; itr; itr = itr->lnk) + if (itr == self) { - if (itr == self) - { - TRACE (interface, "CYCLE"); - cycle = 1; - break; - } - } - - if (!cycle) - { - self->lnk = stack; - stack = self; - - if (self->refcnt == 1) - { - if (self->release) - { - TRACE (interface, "Release %s", self->name); - self->release (self); - } - } - else - { - if (self->deps) - { - TRACE (interface, "Recurse %s %d", self->name, self->refcnt); - - for (LumieraInterface* dep = self->deps; *dep; ++dep) - lumiera_interface_close (*dep); - } - } - - stack = self->lnk; - self->lnk = NULL; - --self->refcnt; + TRACE (interface, "CYCLE"); + cycle = 1; + break; } } -} + if (!cycle) + { + self->lnk = stack; + stack = self; + + if (self->refcnt == 1) + { + if (self->interface->release) + { + TRACE (interface, "Release %s", self->interface->name); + self->interface->release (self->interface); + } + } + else + { + if (self->deps) + { + TRACE (interface, "Recurse %s %d", self->interface->name, self->refcnt); + + for (LumieraInterfacenode* dep = self->deps; *dep; ++dep) + lumiera_interfacenode_close (*dep); + } + } + + stack = self->lnk; + self->lnk = NULL; + --self->refcnt; + } +} /* diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c index 799f84b70..f31755f35 100644 --- a/src/backend/interfaceregistry.c +++ b/src/backend/interfaceregistry.c @@ -22,6 +22,7 @@ #include "lib/mutex.h" #include "lib/error.h" #include "lib/psplay.h" +#include "lib/safeclib.h" #include @@ -41,10 +42,9 @@ NOBUG_DEFINE_FLAG_PARENT (interface_all, backend); NOBUG_DEFINE_FLAG_PARENT (interfaceregistry, interface_all); NOBUG_DEFINE_FLAG_PARENT (interface, interface_all); -static PSplay interfaceregistry; +PSplay lumiera_interfaceregistry; lumiera_mutex lumiera_interface_mutex; - static int cmp_fn (const void* keya, const void* keyb); @@ -52,6 +52,34 @@ static const void* key_fn (const PSplaynode node); +static LumieraInterfacenode +lumiera_interfacenode_new (LumieraInterface iface) +{ + LumieraInterfacenode self = lumiera_malloc (sizeof (*self)); + + psplaynode_init (&self->node); + self->interface = iface; + self->refcnt = 0; + self->lnk = NULL; + self->deps_size = 0; + self->deps = NULL; + + return self; +} + + +static void +lumiera_interfacenode_delete (LumieraInterfacenode self) +{ + if (self) + { + REQUIRE (self->refcnt == 0); + lumiera_free (self->deps); + lumiera_free (self); + } +} + + /** * Initialize the interface registry @@ -63,28 +91,27 @@ lumiera_interfaceregistry_init (void) NOBUG_INIT_FLAG (interfaceregistry); NOBUG_INIT_FLAG (interface); TRACE (interfaceregistry); - REQUIRE (!interfaceregistry); + REQUIRE (!lumiera_interfaceregistry); - TODO ("introduce a registrynode structure, place all dynamic interface stuff there, make interface_struct const"); - - interfaceregistry = psplay_new (cmp_fn, key_fn, NULL); - if (!interfaceregistry) + lumiera_interfaceregistry = psplay_new (cmp_fn, key_fn, NULL); + if (!lumiera_interfaceregistry) LUMIERA_DIE (ERRNO); lumiera_recmutex_init (&lumiera_interface_mutex, "interfaceregistry", &NOBUG_FLAG(interfaceregistry)); } + void lumiera_interfaceregistry_destroy (void) { TRACE (interfaceregistry); - REQUIRE (!psplay_nelements (interfaceregistry)); + REQUIRE (!psplay_nelements (lumiera_interfaceregistry)); lumiera_mutex_destroy (&lumiera_interface_mutex, &NOBUG_FLAG(interfaceregistry)); - if (interfaceregistry) - psplay_destroy (interfaceregistry); - interfaceregistry = NULL; + if (lumiera_interfaceregistry) + psplay_destroy (lumiera_interfaceregistry); + lumiera_interfaceregistry = NULL; } @@ -97,7 +124,7 @@ lumiera_interfaceregistry_register_interface (LumieraInterface self) LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) { TRACE (interfaceregistry, "interface %s, version %d, instance %s", self->interface, self->version, self->name); - psplay_insert (interfaceregistry, &self->node, 100); + psplay_insert (lumiera_interfaceregistry, &lumiera_interfacenode_new (self)->node, 100); } } @@ -113,7 +140,7 @@ lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self) while (*self) { TRACE (interfaceregistry, "interface %s, version %d, instance %s", (*self)->interface, (*self)->version, (*self)->name); - psplay_insert (interfaceregistry, &(*self)->node, 100); + psplay_insert (lumiera_interfaceregistry, &lumiera_interfacenode_new (*self)->node, 100); ++self; } } @@ -128,10 +155,10 @@ lumiera_interfaceregistry_remove_interface (LumieraInterface self) LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) { - REQUIRE (self->refcnt == 0); - psplay_remove (interfaceregistry, &self->node); + LumieraInterfacenode node = (LumieraInterfacenode) psplay_find (lumiera_interfaceregistry, self, 0); + REQUIRE (node->refcnt == 0, "but is %d", node->refcnt); - FIXME ("free deps"); + lumiera_interfacenode_delete ((LumieraInterfacenode)psplay_remove (lumiera_interfaceregistry, &node->node)); } } @@ -147,18 +174,20 @@ lumiera_interfaceregistry_bulkremove_interfaces (LumieraInterface* self) while (*self) { TRACE (interfaceregistry, "interface %s, version %d, instance %s", (*self)->interface, (*self)->version, (*self)->name); - REQUIRE ((*self)->refcnt == 0, "but is %d", (*self)->refcnt); - psplay_remove (interfaceregistry, &(*self)->node); + LumieraInterfacenode node = (LumieraInterfacenode) psplay_find (lumiera_interfaceregistry, *self, 0); + REQUIRE (node->refcnt == 0, "but is %d", node->refcnt); + + lumiera_interfacenode_delete ((LumieraInterfacenode) psplay_remove (lumiera_interfaceregistry, &node->node)); + ++self; - FIXME ("free deps"); } } } -LumieraInterface -lumiera_interfaceregistry_interface_find (const char* interface, unsigned version, const char* name) +LumieraInterfacenode +lumiera_interfaceregistry_interfacenode_find (const char* interface, unsigned version, const char* name) { TRACE (interfaceregistry); struct lumiera_interface_struct cmp; @@ -166,17 +195,24 @@ lumiera_interfaceregistry_interface_find (const char* interface, unsigned versio cmp.version = version; cmp.name = name; - LumieraInterface ret = NULL; + LumieraInterfacenode ret = NULL; LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) { - ret = (LumieraInterface)psplay_find (interfaceregistry, &cmp, 100); + ret = (LumieraInterfacenode)psplay_find (lumiera_interfaceregistry, &cmp, 100); } return ret; } +LumieraInterface +lumiera_interfaceregistry_interface_find (const char* interface, unsigned version, const char* name) +{ + return lumiera_interfaceregistry_interfacenode_find (interface, version, name)->interface; +} + + static int cmp_fn (const void* keya, const void* keyb) { @@ -207,7 +243,7 @@ cmp_fn (const void* keya, const void* keyb) static const void* key_fn (const PSplaynode node) { - return node; + return ((LumieraInterfacenode)node)->interface; } diff --git a/src/backend/interfaceregistry.h b/src/backend/interfaceregistry.h index c6bf4a2e5..0af95c9be 100644 --- a/src/backend/interfaceregistry.h +++ b/src/backend/interfaceregistry.h @@ -22,6 +22,7 @@ #define LUMIERA_INTERFACEREGISTRY_H #include "lib/mutex.h" +#include "lib/psplay.h" #include "lib/interface.h" #include @@ -38,9 +39,37 @@ NOBUG_DECLARE_FLAG (interface_all); NOBUG_DECLARE_FLAG (interfaceregistry); NOBUG_DECLARE_FLAG (interface); +extern PSplay lumiera_interfaceregistry; extern lumiera_mutex lumiera_interface_mutex; +/** + * Interface management node. + * All active interfaces managed through this node which contains the dynamic data for + * dependency tracking and reference counting. + */ +typedef struct lumiera_interfacenode_struct lumiera_interfacenode; +typedef lumiera_interfacenode* LumieraInterfacenode; + +struct lumiera_interfacenode_struct +{ + /** all known interfaces are registered in a tree */ + psplaynode node; + + /** the interface itself */ + LumieraInterface interface; + + /** reference counters and link used for internal reference management */ + unsigned refcnt; + /** temporary used to stack interfaces when recursively opening/closing them */ + LumieraInterfacenode lnk; + /** allocated size of the following deps table */ + size_t deps_size; + /** NULL terminated table of all dependenncies (interfaces opened on initialization) */ + LumieraInterfacenode* deps; +}; + + /** * Initialize the interface registry */ @@ -63,6 +92,9 @@ lumiera_interfaceregistry_remove_interface (LumieraInterface self); void lumiera_interfaceregistry_bulkremove_interfaces (LumieraInterface* self); +LumieraInterfacenode +lumiera_interfaceregistry_interfacenode_find (const char* interface, unsigned version, const char* name); + LumieraInterface lumiera_interfaceregistry_interface_find (const char* interface, unsigned version, const char* name); diff --git a/src/lib/interface.h b/src/lib/interface.h index 16319c244..ef13e40e1 100644 --- a/src/lib/interface.h +++ b/src/lib/interface.h @@ -182,15 +182,10 @@ PPMPL_FOREACH(_P1_, __VA_ARGS__) LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, version, name) = \ { \ { \ - PSPLAYNODE_INITIALIZER, \ #iname, \ version, \ #name, \ sizeof (LUMIERA_INTERFACE_TYPE(iname, version)), \ - 0, \ - NULL, \ - 0, \ - NULL, \ descriptor, \ acquire, \ release \ @@ -342,9 +337,6 @@ struct lumiera_interfaceslot_struct */ struct lumiera_interface_struct { - /** all known interfaces are registered in a tree */ - psplaynode node; - /** name of the interface (type) */ const char* interface; @@ -357,12 +349,6 @@ struct lumiera_interface_struct /** size of the whole interface structure (minor version) */ size_t size; - /** reference counters and link used for internal reference management */ - unsigned refcnt; - LumieraInterface lnk; - size_t ndeps; - LumieraInterface* deps; - /** metadata descriptor, itself a interface (or NULL) */ LumieraInterface descriptor; @@ -391,17 +377,6 @@ struct lumiera_interface_struct */ -/** - * Open an interface by handle. - * This is faster because it needs no lookup, one must be sure to have a valid handle - * which mets the requirements (version) - * @param self pointer to the interface to be opened - * @return self on success or NULL on error - * @internal This function is mostly for internal purposes - */ -LumieraInterface -lumiera_interface_open_interface (LumieraInterface self); - /** * Open an interface by version and name. * Looks up the requested interface, possibly loading it from a plugin. From ee7719eadac375116972e9af2f183ba5da857cf3 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 14 Oct 2008 23:07:58 +0200 Subject: [PATCH 56/66] tests and a first definition of a interface descriptor --- src/lib/interfacedescriptor.h | 48 +++++ tests/backend/test-interfaces.c | 345 +++++++++++++++++++++++++++++--- 2 files changed, 370 insertions(+), 23 deletions(-) create mode 100644 src/lib/interfacedescriptor.h diff --git a/src/lib/interfacedescriptor.h b/src/lib/interfacedescriptor.h new file mode 100644 index 000000000..1b6501a7b --- /dev/null +++ b/src/lib/interfacedescriptor.h @@ -0,0 +1,48 @@ +/* + interfacedescriptor.h - Metadata interface for Lumiera interfaces + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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 LUMIERA_INTERFACEDESCRIPTOR_H +#define LUMIERA_INTERFACEDESCRIPTOR_H + +#include "lib/interface.h" + +/** + * WIP: interface descriptor, needs some generic metadata interface + */ +LUMIERA_INTERFACE_DECLARE (lumieraorg_interfacedescriptor, 0, + /* The following slots are some human-readable descriptions of certain properties */ + LUMIERA_INTERFACE_SLOT (const char*, name, (LumieraInterface)), + LUMIERA_INTERFACE_SLOT (const char*, version, (LumieraInterface)), + LUMIERA_INTERFACE_SLOT (const char*, author, (LumieraInterface)), + LUMIERA_INTERFACE_SLOT (const char*, copyright, (LumieraInterface)), + LUMIERA_INTERFACE_SLOT (const char*, license, (LumieraInterface)) + /* TODO add more things here, dependencies, provisions etc */ + ); + + + +#endif /* LUMIERA_INTERFACEDESCRIPTORS_H */ +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/tests/backend/test-interfaces.c b/tests/backend/test-interfaces.c index fb1c4d30a..214c795c8 100644 --- a/tests/backend/test-interfaces.c +++ b/tests/backend/test-interfaces.c @@ -21,7 +21,7 @@ #include "lib/interface.h" #include "backend/interfaceregistry.h" -//#include "lib/interfacedescriptor.h" +#include "lib/interfacedescriptor.h" #include "tests/test.h" /* @@ -38,6 +38,9 @@ LUMIERA_INTERFACE_DECLARE (lumieraorg_testexample_two, 0, LUMIERA_INTERFACE_SLOT (void, bar2, (const char*)), ); +LUMIERA_INTERFACE_DECLARE (lumieraorg_testexample_void, 0 +); + /* now the functions we want to bind to them @@ -51,32 +54,79 @@ testfunc (const char* message) +LumieraInterface +testacquire (LumieraInterface self) +{ + printf ("Acquire %s_%d_%s\n", self->interface, self->version, self->name); + return self; +} + + +void +testrelease (LumieraInterface self) +{ + printf ("Release %s_%d_%s\n", self->interface, self->version, self->name); +} + /* implementation of some example interfaces */ -LUMIERA_INTERFACE_INSTANCE (lumieraorg_testexample_one, 0, - lumieraorg_standalone_test, - NULL, - NULL, - NULL, - LUMIERA_INTERFACE_INLINE (foo1, "\066\177\042\305\165\243\236\352\164\250\357\307\211\374\123\066", - void, (const char* msg), +LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0, + lumieraorg_tests_descriptor, + /*self reference, yay*/ + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire, + testrelease, + LUMIERA_INTERFACE_INLINE (name, "\073\003\054\127\344\046\324\321\221\262\232\026\376\123\125\243", + const char*, (LumieraInterface iface), + {return "Lumiera Test suite examples";} + ), + LUMIERA_INTERFACE_INLINE (version, "\271\330\345\066\304\217\211\065\157\120\031\365\304\363\364\074", + const char*, (LumieraInterface iface), + {return "No Version";} + ), + LUMIERA_INTERFACE_INLINE (author, "\367\160\342\065\147\007\237\371\141\335\371\131\025\030\257\232", + const char*, (LumieraInterface iface), + {return "Christian Thaeter ";} + ), + LUMIERA_INTERFACE_INLINE (copyright, "\163\106\344\014\251\125\111\252\236\322\174\120\335\225\333\245", + const char*, (LumieraInterface iface), { - printf ("inline\n"); + return + "Copyright (C) Lumiera.org\n" + " 2008 Christian Thaeter "; } ), - LUMIERA_INTERFACE_MAP (bar1, "\162\335\262\306\101\113\106\055\342\205\300\151\262\073\257\343", - testfunc) + LUMIERA_INTERFACE_INLINE (license, "\343\031\207\122\225\217\014\163\015\023\243\101\165\377\222\350", + const char*, (LumieraInterface iface), + { + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) ); + LUMIERA_EXPORT (interfaces_defined_here, LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_one, 0, lumieraorg_first_test, - NULL, - NULL, - NULL, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire, + testrelease, LUMIERA_INTERFACE_MAP (foo1, "\214\310\136\372\003\344\163\377\075\100\070\200\375\221\227\324", testfunc), LUMIERA_INTERFACE_MAP (bar1, "\262\253\067\211\157\052\212\140\114\334\231\250\340\075\214\030", @@ -84,9 +134,9 @@ LUMIERA_EXPORT (interfaces_defined_here, ), LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_two, 0, lumieraorg_second_test, - NULL, - NULL, - NULL, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire, + testrelease, LUMIERA_INTERFACE_MAP (foo2, "\110\152\002\271\363\052\324\272\373\045\132\270\277\000\271\217", testfunc), LUMIERA_INTERFACE_MAP (bar2, "\376\042\027\336\355\113\132\233\350\312\170\077\377\370\356\167", @@ -95,6 +145,131 @@ LUMIERA_EXPORT (interfaces_defined_here, ); +/* + Now we rig a cross dependency test + + we have 4 instances, the respective acquire/release operations set following up: + + one depends on two and three + two depends on one and four + three depends on two and four + four depends on one, two three + + These all are empty interfaces with no slots +*/ + +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) one_keeps_two; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) one_keeps_three; + +LumieraInterface +testacquire_one (LumieraInterface self) +{ + TRACE (tests, "Acquire one %s_%d_%s", self->interface, self->version, self->name); + one_keeps_two = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + one_keeps_three = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); + return self; +} + +void +testrelease_one (LumieraInterface self) +{ + TRACE (tests, "Release one %s_%d_%s", self->interface, self->version, self->name); + lumiera_interface_close ((LumieraInterface)one_keeps_two); + lumiera_interface_close ((LumieraInterface)one_keeps_three); +} + +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) two_keeps_one; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) two_keeps_four; + +LumieraInterface +testacquire_two (LumieraInterface self) +{ + TRACE (tests, "Acquire two %s_%d_%s", self->interface, self->version, self->name); + two_keeps_one = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); + two_keeps_four = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); + return self; +} + +void +testrelease_two (LumieraInterface self) +{ + TRACE (tests, "Release two %s_%d_%s", self->interface, self->version, self->name); + lumiera_interface_close ((LumieraInterface)two_keeps_one); + lumiera_interface_close ((LumieraInterface)two_keeps_four); +} + +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) three_keeps_two; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) three_keeps_four; + +LumieraInterface +testacquire_three (LumieraInterface self) +{ + TRACE (tests, "Acquire three %s_%d_%s", self->interface, self->version, self->name); + three_keeps_two = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + three_keeps_four = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); + return self; +} + +void +testrelease_three (LumieraInterface self) +{ + TRACE (tests, "Release three %s_%d_%s", self->interface, self->version, self->name); + lumiera_interface_close ((LumieraInterface)three_keeps_two); + lumiera_interface_close ((LumieraInterface)three_keeps_four); +} + +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) four_keeps_one; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) four_keeps_two; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) four_keeps_three; + +LumieraInterface +testacquire_four (LumieraInterface self) +{ + TRACE (tests, "Acquire four %s_%d_%s", self->interface, self->version, self->name); + four_keeps_one = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); + four_keeps_two = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + four_keeps_three = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); + return self; +} + +void +testrelease_four (LumieraInterface self) +{ + TRACE (tests, "Release four %s_%d_%s", self->interface, self->version, self->name); + lumiera_interface_close ((LumieraInterface)four_keeps_one); + lumiera_interface_close ((LumieraInterface)four_keeps_two); + lumiera_interface_close ((LumieraInterface)four_keeps_three); +} + + +LUMIERA_EXPORT (dependencytests, + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_void, 0, + lumieraorg_dependencytest_one, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire_one, + testrelease_one + ), + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_void, 0, + lumieraorg_dependencytest_two, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire_two, + testrelease_two + ), + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_void, 0, + lumieraorg_dependencytest_three, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire_three, + testrelease_three + ), + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_void, 0, + lumieraorg_dependencytest_four, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire_four, + testrelease_four + ) + ); + + TESTS_BEGIN TEST ("basic") @@ -117,25 +292,149 @@ TEST ("basic") handle2->foo2 ("this is foo2"); - /* higher level, WIP */ + lumiera_interfaceregistry_bulkremove_interfaces (interfaces_defined_here()); + lumiera_interfaceregistry_destroy (); +} - LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_two, 0, 0, lumieraorg_second_test, handle3); - handle3->bar2 ("this is bar2"); +TEST ("open_close") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (interfaces_defined_here()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_one, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_one, 0, 0, lumieraorg_first_test); + ENSURE (handle); + + handle->bar1 ("this is bar1"); + + lumiera_interface_close ((LumieraInterface)handle); lumiera_interfaceregistry_bulkremove_interfaces (interfaces_defined_here()); lumiera_interfaceregistry_destroy (); } -TEST ("basic") +TEST ("dependencies_one") { + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); + ENSURE (handle); + + TRACE (tests, "Sucessfully opened"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); } -TEST ("basic") + +TEST ("dependencies_two") { + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + ENSURE (handle); + + TRACE (tests, "Sucessfully opened"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); } -TEST ("basic") +TEST ("dependencies_three") { + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); + ENSURE (handle); + + TRACE (tests, "Sucessfully opened"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); +} + + +TEST ("dependencies_four") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); + ENSURE (handle); + + TRACE (tests, "Sucessfully opened"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); +} + + + +TEST ("dependencies_all") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + TRACE (tests, "OPEN one"); + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_one = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); + ENSURE (handle_one); + + TRACE (tests, "OPEN three"); + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_three = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); + ENSURE (handle_three); + + TRACE (tests, "OPEN two"); + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_two = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + ENSURE (handle_two); + + TRACE (tests, "OPEN four"); + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_four = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); + ENSURE (handle_four); + + TRACE (tests, "Sucessfully OPENED"); + + TRACE (tests, "CLOSE four"); + lumiera_interface_close ((LumieraInterface)handle_four); + + TRACE (tests, "CLOSE two"); + lumiera_interface_close ((LumieraInterface)handle_two); + + TRACE (tests, "CLOSE three"); + lumiera_interface_close ((LumieraInterface)handle_three); + + TRACE (tests, "CLOSE one"); + lumiera_interface_close ((LumieraInterface)handle_one); + + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); +} + + + +TEST ("highlevel, plugin") +{ + + } TESTS_END From 29f5a0f2abbeb32b305228d2fe9dd92d28a377a7 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 15 Oct 2008 00:02:22 +0200 Subject: [PATCH 57/66] moved the remaining interface stuff from lib to backend --- src/backend/Makefile.am | 2 ++ src/backend/interface.c | 3 ++- src/{lib => backend}/interface.h | 0 src/{lib => backend}/interfacedescriptor.h | 2 +- src/backend/interfaceregistry.h | 3 ++- src/lib/Makefile.am | 2 -- tests/backend/test-interfaces.c | 4 ++-- 7 files changed, 9 insertions(+), 7 deletions(-) rename src/{lib => backend}/interface.h (100%) rename src/{lib => backend}/interfacedescriptor.h (98%) diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 78ffea592..12110b26f 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -46,7 +46,9 @@ noinst_HEADERS += \ $(liblumibackend_a_srcdir)/filehandle.h \ $(liblumibackend_a_srcdir)/filedescriptor.h \ $(liblumibackend_a_srcdir)/filehandlecache.h \ + $(liblumibackend_a_srcdir)/interface.c \ $(liblumibackend_a_srcdir)/interfaceregistry.h \ + $(liblumibackend_a_srcdir)/interfacedescriptor.h \ $(liblumibackend_a_srcdir)/config.h \ $(liblumibackend_a_srcdir)/configentry.h \ $(liblumibackend_a_srcdir)/configitem.h \ diff --git a/src/backend/interface.c b/src/backend/interface.c index 0150e403e..58d0d0c0f 100644 --- a/src/backend/interface.c +++ b/src/backend/interface.c @@ -21,7 +21,8 @@ #include "lib/mutex.h" #include "lib/safeclib.h" -#include "lib/interface.h" + +#include "backend/interface.h" #include "backend/interfaceregistry.h" diff --git a/src/lib/interface.h b/src/backend/interface.h similarity index 100% rename from src/lib/interface.h rename to src/backend/interface.h diff --git a/src/lib/interfacedescriptor.h b/src/backend/interfacedescriptor.h similarity index 98% rename from src/lib/interfacedescriptor.h rename to src/backend/interfacedescriptor.h index 1b6501a7b..468bd2e80 100644 --- a/src/lib/interfacedescriptor.h +++ b/src/backend/interfacedescriptor.h @@ -21,7 +21,7 @@ #ifndef LUMIERA_INTERFACEDESCRIPTOR_H #define LUMIERA_INTERFACEDESCRIPTOR_H -#include "lib/interface.h" +#include "backend/interface.h" /** * WIP: interface descriptor, needs some generic metadata interface diff --git a/src/backend/interfaceregistry.h b/src/backend/interfaceregistry.h index 0af95c9be..11ed91bde 100644 --- a/src/backend/interfaceregistry.h +++ b/src/backend/interfaceregistry.h @@ -23,7 +23,8 @@ #include "lib/mutex.h" #include "lib/psplay.h" -#include "lib/interface.h" + +#include "backend/interface.h" #include diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 2ad293158..4dc927b43 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -31,7 +31,6 @@ liblumi_a_SOURCES = \ $(liblumi_a_srcdir)/psplay.c \ $(liblumi_a_srcdir)/mrucache.c \ $(liblumi_a_srcdir)/time.c \ - $(liblumi_a_srcdir)/interfacedescriptor.c \ $(liblumi_a_srcdir)/appconfig.cpp noinst_HEADERS += \ @@ -47,7 +46,6 @@ noinst_HEADERS += \ $(liblumi_a_srcdir)/time.h \ $(liblumi_a_srcdir)/ppmpl.h \ $(liblumi_a_srcdir)/interface.h \ - $(liblumi_a_srcdir)/interfacedescriptor.h \ $(liblumi_a_srcdir)/appconfig.hpp \ $(liblumi_a_srcdir)/lifecycleregistry.hpp diff --git a/tests/backend/test-interfaces.c b/tests/backend/test-interfaces.c index 214c795c8..09fa25afa 100644 --- a/tests/backend/test-interfaces.c +++ b/tests/backend/test-interfaces.c @@ -19,9 +19,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "lib/interface.h" +#include "backend/interface.h" #include "backend/interfaceregistry.h" -#include "lib/interfacedescriptor.h" +#include "backend/interfacedescriptor.h" #include "tests/test.h" /* From 0cf7dec7938811aaf7abf51d3d3d9355cf82c7e0 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 19 Sep 2008 15:45:24 +0200 Subject: [PATCH 58/66] Interfaceregistry for 'active' interfaces All interfaces which are available get registered in a tree. This will be used internal for lookup interfaces. The higher level calls will be an amalgamation of this lookup and the upcoming plugindb and loader. --- src/backend/Makefile.am | 2 +- src/backend/interfaceregistry.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 12110b26f..cf5fe1897 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -46,7 +46,7 @@ noinst_HEADERS += \ $(liblumibackend_a_srcdir)/filehandle.h \ $(liblumibackend_a_srcdir)/filedescriptor.h \ $(liblumibackend_a_srcdir)/filehandlecache.h \ - $(liblumibackend_a_srcdir)/interface.c \ + $(liblumibackend_a_srcdir)/interface.h \ $(liblumibackend_a_srcdir)/interfaceregistry.h \ $(liblumibackend_a_srcdir)/interfacedescriptor.h \ $(liblumibackend_a_srcdir)/config.h \ diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c index f31755f35..ffa979340 100644 --- a/src/backend/interfaceregistry.c +++ b/src/backend/interfaceregistry.c @@ -80,7 +80,6 @@ lumiera_interfacenode_delete (LumieraInterfacenode self) } - /** * Initialize the interface registry */ From f7fa5769cafa87cfe8b0ad2b258ae10cd76adac3 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 22 Sep 2008 22:38:17 +0200 Subject: [PATCH 59/66] Start of a 'wordlist' type for the config subsystem wordlists are simple not quoted words delimited by semicolon, tab, space or commas. Some special functions will allow to access each of this words by index etc. --- src/backend/config.h | 1 - src/backend/config_wordlist.c | 6 ++++-- tests/22config_highlevel.tests | 22 ++++++++++++++++++++++ tests/backend/test-config.c | 1 - 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/backend/config.h b/src/backend/config.h index 0b50c7da2..4e3acd9a9 100644 --- a/src/backend/config.h +++ b/src/backend/config.h @@ -217,7 +217,6 @@ lumiera_config_setdefault (const char* line); LUMIERA_CONFIG_TYPES #undef LUMIERA_CONFIG_TYPE - /** * Wordlists * Wordlists are lists of single words delimited by any of " \t,;". diff --git a/src/backend/config_wordlist.c b/src/backend/config_wordlist.c index 548a4d824..6cd0389a7 100644 --- a/src/backend/config_wordlist.c +++ b/src/backend/config_wordlist.c @@ -33,6 +33,9 @@ extern LumieraConfig lumiera_global_config; //TODO: System includes// +/** + * return nth word of a wordlist + */ const char* lumiera_config_wordlist_get_nth (const char* key, unsigned nth) { @@ -196,12 +199,11 @@ lumiera_config_wordlist_append (const char* key, const char** value, unsigned nt { } + LumieraConfigitem lumiera_config_wordlist_preprend (const char* key, const char** value, unsigned nth) { } - - #endif diff --git a/tests/22config_highlevel.tests b/tests/22config_highlevel.tests index 3d131da3e..c43baaefe 100644 --- a/tests/22config_highlevel.tests +++ b/tests/22config_highlevel.tests @@ -222,3 +222,25 @@ TEST "wordlist add to empty list, same" wordlist_add 'foo.bar' '' same same << E out: ' same' out: ' same' END + + +TEST "wordlist get item from empty list should fail" wordlist_get_nth 'foo.bar' '' 0 << END +out: 'NULL' +END + +TEST "wordlist get item past end should fail" wordlist_get_nth 'foo.bar' 'baz barf gnarf' 3 << END +out: 'NULL' +END + +TEST "wordlist get first item" wordlist_get_nth 'foo.bar' 'baz barf gnarf' 0 << END +out: 'baz' +END + +TEST "wordlist get last item" wordlist_get_nth 'foo.bar' 'baz barf; gnarf' 2 << END +out: 'gnarf' +END + +TEST "wordlist get middle" wordlist_get_nth 'foo.bar' 'baz barf, gnarf' 1 << END +out: 'barf' +END + diff --git a/tests/backend/test-config.c b/tests/backend/test-config.c index deffa7c00..506801454 100644 --- a/tests/backend/test-config.c +++ b/tests/backend/test-config.c @@ -409,5 +409,4 @@ TEST ("wordlist_add") } - TESTS_END From 9943dd76fdfd1921010bb968dcbe0ddf45f51c76 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sun, 28 Sep 2008 03:49:28 +0200 Subject: [PATCH 60/66] use a recursive mutex to lock interface operations locking interfaceregistry operations and later on open/close etc --- src/backend/interfaceregistry.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/interfaceregistry.h b/src/backend/interfaceregistry.h index 11ed91bde..02481072d 100644 --- a/src/backend/interfaceregistry.h +++ b/src/backend/interfaceregistry.h @@ -70,6 +70,8 @@ struct lumiera_interfacenode_struct LumieraInterfacenode* deps; }; +extern lumiera_mutex lumiera_interface_mutex; + /** * Initialize the interface registry From be9d2b189f695cea6518b2fd23b4f322e2a196d6 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 15 Oct 2008 18:01:52 +0200 Subject: [PATCH 61/66] fix lib/Makefile.am, a stale interfaces.h was left here --- src/lib/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 4dc927b43..55d88e71a 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -45,7 +45,6 @@ noinst_HEADERS += \ $(liblumi_a_srcdir)/mrucache.h \ $(liblumi_a_srcdir)/time.h \ $(liblumi_a_srcdir)/ppmpl.h \ - $(liblumi_a_srcdir)/interface.h \ $(liblumi_a_srcdir)/appconfig.hpp \ $(liblumi_a_srcdir)/lifecycleregistry.hpp From 510456d25002b95932c613ad3aec3fae912d2714 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 15 Oct 2008 18:27:34 +0200 Subject: [PATCH 62/66] some cleanup on the main TiddlyWiki, added latest protocol --- wiki/draw/Lumi.Architecture-1.png | Bin 0 -> 83409 bytes wiki/draw/LumiLogo.png | Bin 2345 -> 2111 bytes wiki/index.html | 410 +++++++++++++++++++++++------- 3 files changed, 315 insertions(+), 95 deletions(-) create mode 100644 wiki/draw/Lumi.Architecture-1.png diff --git a/wiki/draw/Lumi.Architecture-1.png b/wiki/draw/Lumi.Architecture-1.png new file mode 100644 index 0000000000000000000000000000000000000000..684d8c45ab0ab17b050179c8d96d0f6ebd31268c GIT binary patch literal 83409 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 literal 0 HcmV?d00001 diff --git a/wiki/draw/LumiLogo.png b/wiki/draw/LumiLogo.png index 1fd263aed1c9fc761b34bd79b337eaffcee5b004..a072e96cb61514c91bf5082d27e9673c9386c55e 100644 GIT binary patch literal 2111 zcmbVN`8(8$7yejo4HCwdp=OAYy>h89N*H4XSBxb}hGZFIof(Z~$jy>nnHfY>$a2ff zweMS|WQoc$b`qv!8*4Y)b^nF$dCvR3&pGGE^PcADkf+N}!@X~@W!hSaeG%WxD?B*W5|@;cbv@KQ z3f(+Nb6*~2n~5)w3Lw$#-52A$Jvl?_7H%FApOfEJ=Q6x$7j)0@mER@G6%&eNcJzQODE2aORmV(s-3X_x;k@}IbJrxXR zr(w`zN-(~~D5GmJG%a^ULj;U-9$fUOj&9ME&FA74q@xJ*gt<>7!yYL~aud$0JhQfB zKy%$5n=LB2Dmsd*2+J0(8`B(308w)<%*(|rgaIxKwW>m?B9*PKx&;{KoNlVZ z}_8^IRXR9P$vPo^+&`0po`G_6gduv)-;C|2J(+(GQIeX<@TjN3o zn;&kLb2^`OAyJ{5zszq#d<5H3R+lo=ix=*<5a?CVCTa=SGKGB)H=f?*uecv;Z6`xBQ^2_)* zOpgs?+NS=g){&$}{9d9GgMKYl$GxkNH<%0N)LpO!-@Fwpn|Z2^^^Dwo0p)?}=yj&l zAAJ2;uHTuwYgocUl7r<&94DtHM=NXaKOSp`LPAR%&QJ@7aJ|~@hT+#umas`$wHHKD zfz8vWtrvs)D%OU#fFbeBkcbzhfFct30%jBQEj*FqAWIRLa&JDGMql z%+zEl4N}D9g}qdug?Af|y38O8n94r{d4T5_T#<9V2(W^`i{L3?hzeI)2urcgpAu+r z&JDj>l=X1n3C(_pZpET@5@}l;ZxR!j3o&!%sTXuQhiMRaafpO5zvj1ONWC?44RvUgVkC&Ak;_=M!hzF!_$4UE|9CykQ{nH?B%@Q_fm!;5>nf(VLTE7t&I{W%bp) z4b|j-e!z4wrDVh0hLW5>stW;o{!61fle2LZ#Z!Z#;XN9CBJ%!oAW;8;4nK<)K3MFk zc*ubeBj(kglHjId>3ITTQT`hjw~@~BQP=uHb)=|sD+mLb;H2F!c1ypF(F!4{QQjD6 ztK1Q#{vgkJZ{lj}oIIaa#nimf=-Wt}lr_qWZEY^tpShn+|J+4_2M?J!D&(;$)%Os2 zI#(v%JJ!aC!qIab=1U0ElAL=rP)Zk!jut>w*ENiSKZqPHgD>Uk&IH&P02E;c9SV&+ zuCM;3luU?*E;8qO*0H%au_aW5wriW~Z}xleB`o zC_$%P@=mds{H0|$#Beyv6wEDj&^pF5QI$l#B8?I27F4B&FTRB-Zn(5EZiF24`@<7% z&icp!2a6KXMuU?1ZVq;0;o>}VmuG^dZj3iMzjG47p6OM^+{W&ho@R(X;K$nwc{$+g zx1_bsOb33M<)-THf2~%l9*sW6XyTZa-J>|2Y*%7v@z42|ZD(ODJ>bt)O8qUoFeiXt zp+7m<~sQn2WDt1E~5I-jmh3O1!`c zkL&wqW4=%SHEzdpmY#_VoL~J#Pf3gXlJiJ`z1mxeO0#WE9DVjG?TI2I$)}!$FM;(m zm-Gu@ix1)KmaP47DQo${ zZSOi)uGtHfcKmiABNaM6CV#DV2fCvDTD1Lwr zEbOmlW;PF(&wqQ*)(dY0fO}O?z4ILk(;JNr0KjJUtO+I;dfOywv1qd`1MdB|tA8H0 zr}B-PJO3yJZS~lt&+B;xoGLUJ%$I=nSH4dp{!Yk3&o;Cjh9!XUg2BF5*DY$b?6~P5 zl~%t;>j62a($&ooBGi6IX%?Mp(Ubw>W^`@&!P%|)J#Qw3m*^WY4%KV;s#neA+i363 z66+fCs0M8PS0#DNCy7p}QG1}XTus1|ukAG0+7eauSDQ&~P(tQi0iG<~g#Z8m literal 2345 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
+
+
This page intends to capture the lage picture regarding Lumiera's Architecture. This may be a moving target...
+
+[img[Overview of Lumiera Architecture|draw/Lumi.Architecture-1.png][http://www.lumiera.org/gitweb?p=LUMIERA;f=doc/devel/draw/Lumi.Architecture-1.svg;hb=HEAD]]
+
+//this drawing is maintained as SVG in GIT//
+~~(click on the above image...)~~
+
+!Description
+* the Application has three Layers: [[Backend|backend.html]], [[Proc-Layer|renderengine.html]] and GUI
+* the Application shall be completely functional without GUI ("headless", script-driven)
+* all IO, media data fetching, processing and bookkeeping falls within the realm of the Backend
+* all media object manipulation, deciding and configuration is the Proc Layer's job
+* extensible by plugins on all levels, highly configurable, but not totally componentized (micro kernel) architecture
+* strong separation between high-level and low-level areas of the Application
+* the user/GUI manipulates a [[high-level model|renderengine.html#HighLevelModel]] whereas rendering is based on a corresponding [[low-level model|renderengine.html#OverviewRenderEngine]]
+* stored Session (state) is comprised of high-level model, a collection of [[Assets|renderengine.html#Asset]] and accompaning configuration
+* (possibly) several storage backends, abstracted out by a common interface
+
+
+
* autotools 1.10 (as of 5/2008) &dash; Question: other versions usable too?
 * the usual procedure, in short
@@ -802,14 +823,12 @@ for __Running__
LumieraWiki
 ShortCuts
-
+
* There is a [[Manifest]] explaining the vision of the Lumiera project
 * The foundation how we work together is defined in LumieraDesignProcess
 * There is a description how the git repository is set up in RepositorySetup
 * we decided to write code in GNU style, with no tabs (use spaces)
-
-!basic decisions
-* TimeHandling
+
/***
@@ -954,9 +973,11 @@ git push git://git.pipapo.org/lumiera/mob
 
 lumiera/mob is an anonymous account at pipapo.org where everyone can commit changes. 
-
+
We keep a protocol or short summary of each important discussion. The summaries of the monthly developer meetings are posted to the Mailinglist and can be found on pipapo.org too
 
+* [[10-08 developer meeting 10.Oct.2008|IRC_2008-10-10]]
+* [[07-08 developer meeting 6.Jul.2008|IRC_2008-07-06]]
 * [[06-08 developer meeting 5.Jun.2008|IRC_2008-06-05]]
 * [[05-08 developer meeting 8.May.2008|IRC_2008-05-08]]
 * [[04-08 developer meeting 3.Apr.2008|IRC_2008-04-03]]
@@ -968,12 +989,12 @@ lumiera/mob is an anonymous account at pipapo.org where everyone can commit chan
 * [[buffer allocation for render nodes (6/08)|IRC_log_BufferAllocation_2008-06]]
 
-
+
!10/11 aug 2007
 * maybe let the render graph builder generate a meta language which then is ''jit compiled'' for each configuration
 * using smart pointers and factories for objects ''and avoid unprotected use of {{{new}}} and {{{delete}}}'', if this wont work because of cycles, we might investigate specialized garbage collectors for specific cases.
-* format for frames would be likely ffmpeg or such first, finally we see what suits best. We have to provide coverter nodes to convert frame formats for accessing diffrent libraries anyway (ffmpeg, v4l, gstreamer, ...)
-* we need a pool of worker threads and associated api's
+* format for frames would be likely ffmpeg or such first, finally we see what suits best. We have to provide coverter nodes to convert frame formats for accessing different libraries anyway (ffmpeg, v4l, gstreamer, ...)
+* we need a pool of worker threads and associated ~APIs
 * accessing frames has a time (get until..), ''unique frame identifier'' (see below), policies (what to do when out of time, quality required,..) and hints (recursive dependency, must cache, playback speed and direction, ...)
 * for each sequence (configuration) each node has a number (monotonic incrementing), a generation counter (increment on each parameter change), a propagation sum (from parent nodes) 
 * frames are identified by their time(frame number) and node number plus generation number and propagation sum. This allows easily to find out when a frame has to be rerendered
@@ -981,20 +1002,20 @@ lumiera/mob is an anonymous account at pipapo.org where everyone can commit chan
 ** viewer is just a decoder where a display window attaches
 ** one can have multiple of such display windows
 ** tracks, effects, (things which are nodes in the render graph) can add gui components to the viewer, like masking tools, panning, overlay and other compositor things.
-** in the render graph we have ''attachement points'' i.e. ~ExitNode-instances. Display and other output can only be pulled from such ~ExitNodes. Changing just the display or attaching it to another ~ExitNode doesn't count as change of the render graph, while reordering or reconfiguring the ~ExitNodes themselfes of course //is// a reconfig.
+** in the render graph we have ''attachment points'' i.e. ~ExitNode-instances. Display and other output can only be pulled from such ~ExitNodes. Changing just the display or attaching it to another ~ExitNode doesn't count as change of the render graph, while reordering or reconfiguring the ~ExitNodes themselves of course //is// a reconfiguration.
 * tracks are just containers for other nodes
-** they serve a gui representation (timeline, patchbay, viewer)
+** they serve a GUI representation (timeline, patchbay, viewer)
 ** they do the mixing of contained things
-** can be recursive, the gui represents basically a tree
-** we need some 'wireing' abstraction for the gui to make it a real graph
+** can be recursive, the GUI represents basically a tree
+** we need some 'wiring' abstraction for the GUI to make it a real graph
 * rendering frames, context between frames
 ** the proc layer ''only queries frames'' (by identifier and timeout) the backend tries to serve the best it can do (from cache or let them render)
 ** each render job carries some ''quality limit'' (as S/N ratio) when previewing or scratching through the project this is used to generate reasonable quality previews
-** individual processing nodes can use additional state for their processings...
-*** the node objects themselfs should stay stateles, i.e. they shouldn't store state internally. 
+** individual processing nodes can use additional state for their calculations...
+*** the node objects themselves should stay stateless, i.e. they shouldn't store state internally. 
 *** they can use special ''context frames'', which are provided and managed by the backend (as opaque data blocks).
 *** they can depend on previous state, i.e request from the backend a previous context frame, the same way as they can request previous media frames from the bakend, but there is no guarantee that the backend will satisfy this request.
-* on the question who decides what to do after a cache miss, we tend to put the decision into the render node (because this seems to be the simpler aproach), but still have to work out the details.
+* on the question who decides what to do after a cache miss, we tend to put the decision into the render node (because this seems to be the simpler approach), but still have to work out the details.
 
@@ -1673,6 +1694,272 @@ While in this discussion, several aspects of the cooperation of GUI and Proc lay Next meeting: ''Thursday 3.July 2008 16:30 UTC''
+
+
! 6.July 2008 on #lumiera
+__Participants__
+* ichthyo
+* cehteh
+* jilt
+* dmj726
+* Teld
+* _nasa_
+* alcarinque_
+* MNO
+* Wescotte
+* weatherhead
+
+//Protocol written by Teld//
+
+!Boilerplate
+!!Organization of this meeting
+* dmj726 intends to write the protocol
+* ichtyo is chairman
+* there is no agenda, so this is a freeform meeting
+!!Leftovers from last meeting
+//There are no leftovers//
+
+!Introduction of Lumiera
+Because there are quite some new participants, an introduction of the project Lumiera is given
+
+There are 3 core devs:
+* cehteh: backend serving data (from filesystem and so on), manages threads, using C, Posix System programming, maintains autotools and git
+* ichthyo: proc layer, render graph, in the middle, C++, he maintains scons
+* joelholdsworth: GUI in C++/Gtkm
+Other people involved:
+* rcbarnes: ui designer and HCI expert
+* raffa: web content
+* Simav: gives a hand when and where he can
+* Teld: web infrastructure
+
+The foundations of the design are already done but a lot of detail needs to be worked out. cehteh and ichtyo provide a non exhaustive list.
+
+cehteh:
+* improvement of the testsuite (simple Bash)
+* start of a worker thread manager (Posix knowledge required)
+* start of the scheduler (some Posix, good C coding)
+* runtime profiler (little but advanced Posix realtime programming job)
+* review of code and documentation
+* system administration
+* setup of a build/test environment on the server
+* setup and maintain postfix/dovecot by a mail administrator
+ichtyo:
+* asset management (keeping track of all media files, captures, clips, scenes)
+* session loading saving and the issues of compound documents
+* session structure related issues to be able to Import/Export/Connect via e.g. OSC (Open Sound Control) or JACK
+* flesh out the more high level "edit operations" and the interface to UNDO
+* Prolog integration after the first integration round has been reached. The Prolog interpreter will do some of the more advanced configuration (example: if effect XYZ is used, then deinterlace beforehand).
+* integration with some sort of production support software (like Celtx)
+cehteh emphasizes that Lumiera is an open project, so anybody can jump in where he sees fit as long as he communicates and acknowledges with the persons involved. ichtyo points out that the plugin structure is very important: anything that is not legally completely clean (proprietary), should be factored out into a sister project and be loaded as plugin.
+
+!Issues and questions that come up
+!!handling of config errors.
+When the configuration has a syntax error, in 90% of the cases there will be no possibility to do anything sane. Therefore, the config system will be built to log a warning and the user code does not need to care. The user just gets an alert and the application continues to work.
+
+!!scripting language.
+There will be a scripting interface. ichtyo does not want scripts everywhere, only at well defined interfaces. That implies also that scripts cannot just do anything, only that what is permitted in a controlled way. The meeting agrees on that. cehteh wants one default language and proposes Lua: light, simple.
+
+Other members suggestions: Python, Ruby, Scheme. However, Python and Ruby are very slow. Scheme has many variants.
+
+!!editing on small devices (eeePC)
+Problem: video editors GUIs are some of the most elaborate and complicated GUIs. However, basic functions consist of only a few buttons. Proxy editing could be a solution. It is decided that it is not a primary goal now. First the basics have to be implemented.
+
+!!uWiki.
+uWiki is the glue between a markup driver (Asciidoc) and revision control (git). Haserl with Bash is used now. Haserl appears to have problems with conditional includes. Its limits have been reached while prototyping. Lua could very well be a valid replacement. It will be investigated. PHP is rejected because it is not stable and suffers from serious security problems.
+
+!!'musical' timeline in bars and beats
+The questions of syncing and linking things together are already on the core agenda: the so-called placement concept. Discussion is still going on, especially with the GUI developer joelholdsworth. See for detailed information: http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ProcPlacementMetaphor
+
+!Next meeting
+The next meeting will be held ''Thursday, 7 August 19:00 UTC''
+
+
+
+
!Oct 10, 2008 on #lumiera
+19:00 - 23:15 UTC
+__Participants__
+* cehteh
+* ichthyo
+* joelholdsworth
+* alcarinque
+* ~KenSentMe
+* Plouj
+* raffa
+* thorwil
+* Victor_Sigma
+* wildhostile
+ 
+//Protocol written by Ichthyo//
+
+!!!organisational
+Not much of a fixed agenda this time.
+Agreement to start with the Logo discussion, as this is of general interest, followed by the design drafts and similar dev topics.
+
+!The Lumiera Logo
+Summary of the situation: discussion regarding a logo for Lumiera is going on sporadically since quite some time. Several people from the community have made proposals. Also, there was discussion about the criteria a logo would have to fulfil. Especially the core devs raised the bar and required quite some professional level of design. On the contrary, several members of the community were concerned that such a demanding attitude will hinder creativity in a domain which is far off from coding. Moreover, many people complained they are really excited about Lumiera and strongly want to participate in some manner, but find it very difficult in the current phase of the project to give any valuable contribution.
+
+This summer, one of the proposals by [[Leandro Ribeiro|http://farm4.static.flickr.com/3060/2927333034_ac94be80ea_b.jpg]] gained some popularity and especially was embraced by two of the core devs, while the GUI core dev wasn't convinced and [[explained|http://www.pipapo.org/pipawiki/JoelHoldsworth/LogoThoughts]] his reservation. Prior to this meeting some people joined a brainstorming session and came up with [[another design|http://www.pipapo.org/pipawiki/Lumiera/Logos?action=AttachFile&do=get&target=combo.png]] compiled of several proposals, which could meet the acceptance of the core devs. At the same time, Raffa made an argument for conducting a public contest, similar to the one which gave us the name of Lumiera. The situation for Lumiera is somewhat special. Usually, the community builds when the product is already minimally usable; we can't have users for now, but we have a lot of prospective users.
+
+Thus, while basically it would be possible for the core devs to shorten the process by just picking a design which is acceptable for all, maybe after augmenting it a little, several of the arguments articulated this far are in favour of a more formal decision by a contest:
+* it would allow for a lot of people to really participate
+* it could help to shape a general (graphic) style for Lumiera
+* it could underpin the fact Lumiera indeed is a collaborative effort
+* it doesn't mean additional work for the core devs on the whole
+* it helps spreading the word
+
+Then, there is some discussion about the requirements. The core devs are concerned to keep up some quality level, because there is the possibility for the community to embrace a design, but when Lumiera enters the area it is intended to do, the standards of comparison will be different. The GIMP logo can be quoted as a negative example here.
+
+!!Conclusion
+There will be a Lumiera Logo contest.
+* we should further discuss requirements on the Mailinglist until the end of the next week
+* the ''deadline for submissions'' will be the next meeting (Nov 12 2008)
+* then, after a pre-selection phase, the vote shall be conducted prior to the December meeting.
+
+Some minimal technical requirements will be enforced:
+* the basic sign fits a compact rather quadratic bounding box (like 4:3)
+* easy to print on various media (on posters, t-shirts, ..)
+* basically a shape, using only a small number of solid colours (web safe)
+* should work on different backgrounds, esp. not requiring white background
+* the design should scale from microscopic size (favicon) up to big posters
+* vector graphic source is mandatory
+
+Besides, we give some artistic guidelines
+* it should be recognisable
+* it should contain something like a sign, not just "Lumiera" in a fancy font
+* it should not rely on transparencies, gradients and subtle shades,
+* it should be viable, possibly work well in different presentation styles, able to be simplified as well as graphically augmented
+
+Raffa volunteers to help organizing the contest and the voting.
+----
+
+
+
+
+!Recurring Topics: design proposals
+Discussion of open [[design process|http://www.pipapo.org/pipawiki/Lumiera/DesignProcess]] drafts.
+
+!!Mistakes to avoid
+[http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/MistakestoAvoid Mistakes to avoid in the Lumiera Design]
+We are carrying this one along since quite some time and we'd like to get rid of it, either by reworking it or by dropping it as-is. Because it contains a mixture of things
+* we fully agree to 80% of the statements made there, but we think those statements are so very basic and self-evident as to be considered off-topic here. We are aware of the recurring problems with open source video editing. That's why we are here.
+* the proposal draws conclusions on two technically substantial points, at which we don't agree. And it fails to provide sufficient (technically sound) arguments to prove these statements.
+
+While it is certainly //desirable// to be cross-platform as much as possible and especially ''target Microsoft Windows'', we don't see much possibilities with today's mainstream technology to build an application which is as technologically demanding as a video editor is. We would end up developing two or even three sister applications, or we are forced to sacrifice performance for portability. When put up to face such options, we have a clear preference to concentrate on a really free and open platform.
+
+While it is certainly //desirable// to make the application as robust as possible, we don't see how ''using multiple separate processes'' could help us with this goal //without creating major scalability or performance problems// due to the use of shared memory. And, yet more important: we don't share the basic assumption made in the proposal, namely that video processing is inherently dangerous. We think the basic algorithms involved are sufficiently well-known and understandable to implement them in a sound manner.
+
+__Conclusion__: drop it
+
+!!!!on the question of separate processes
+The only practical solution would be to separate the GUI. Separating backend and proc-layer doesn't make much sense, technically speaking. We re-considered this question. Joelholdsworth (GUI) and Ichthyo (Proc-layer)prefer running the GUI in-process and to avoid the additional hassle with shared memory. Cehteh (backend) doesn't care for know and would like to re-discuss it as an option later on. This is somewhat similar to the possibility of running Lumiera distributed over the network, which is a goal, but not an immediate one.
+
+
+!!Tag Clouds
+[http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/TagCloudsOnResources Tag clouds on resources]
+Tags are considered very important. Meanwhile, we have a much more advanced proposal, which superseeds this one: [http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/DelectusShotEvaluator "Delectus"]
+
+__Conclusion__: drop it
+
+
+
+!!Overview of Lumiera Architecture
+[http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ArchitectureOverview Architecture Overview]
+The purpose of this drawing to give an first impression what subsystem is where and what the names mean. Since discussed last time, Ichthyo re-arranged the plugins as requested and added some details for the backend. Joelholdsworth points out that it's OK for him to show the GUI just as a single block here (and of course the GUI has much more internal structure). Cehteh adds that maintaining this drawing surely is a moving target, so we better remove the rendered image and just retain the textual description and link to the source (SVG), which is in GIT.
+
+__Conclusion__: accept it, change the image to a link
+
+
+
+!!EDLs as Meta-Clips
+[http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/EDLsAreMetaClips EDLs are meta-clips]
+This is just a high-level proposal, which doesn't go into technical details of implementing nested EDLs. It just poses the question "do we want to treat nested EDLs as being meta-clip" -- which we do.
+
+__Conclusion__: accepted
+
+
+
+!!The Builder
+[http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ProcBuilder Builder in the Proc-Layer]
+Similar to the previous one, this is a high-level proposal. It covers the fundamental approach Ichthyo takes within the Proc-Layer. Cehteh adds that he agrees to 98% and the remaining 2% are just matter of favour. Personally, he would have preferred one large graph which gets garbage collected (instead of the segmented graph)
+
+__Conclusion__: accepted
+
+
+
+!!High-level Model
+[http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ProcHighLevelModel Overview of the High-level model within the Proc-Layer]
+Cehteh queries if this shouldn't be better moved over to the documentation? He is fine with the contents, but it seems to be a bit voluminous for a design proposal. Ichthyo asks to leave it there just for now, as he feels it still needs review.
+
+__Conclusion__: leave it for now, maybe retract it from the design proposals and file it as documentation?
+
+
+
+!!Lua scripting language
+[http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ScriptingLanguage use Lua as required scripting language]
+All core devs agree with this decision. Joelholdsworth points out that he is fine with Lua, just he didn't want to write the GUI in it. Ichthyo adds that Lua is probably the best choice from today's mainstream scripting languages, because it is very lightweight. He further points out, that having Lua as //required// scripting language doesn't rule out using other popular languages (Python, Ruby) for scripting. Just they aren't required for running Lumiera. Cehteh will have a look at the details as soon as possible, but has currently other more urgent things in the queue. (Plouj shows interest to help here)
+
+__Conclusion__: accepted
+
+
+
+!!Time Handling
+[http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/time_handling time handling]
+A long standing proposal; meanwhile we've decided to build on GAVL, which is now reflected in the text of this proposal too. Ichthyo points out he changed the rounding rule in this proposal from "mathematical" to "floor (x+0.5)". Cehteh asks if we should use a thin library layer on top of gavl, to centralize all the time calculations. There seems to be agreement that this should actually be done //on demand.// Joelholdsworth sais sometimes he'd prefer to have a simple raw type to represent time, because it makes calculations much easier. Ichthyo adds that internally (within the layers, but not on interfaces) one could use a thin C++ wrapper with overloaded operators, which is default-convertible to gavl_time.
+
+__Conclusion__: accepted
+
+Note: the proposed rigid testsuite for time handling is necessary only when we introduce a wrapper...
+
+!!Interface naming convention
+See the design proposal [http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/InterfaceNamespaces Interface Namespaces]. While working on the plugin loader, ''Cehteh'' and ''nasa'' did some brainstorming about a plugin naming scheme and how to get unique interface identifiers. The idea is to use a distinctive prefix, like a condensed organisation domain or email name, or a GPG fingerprint or even a UID, followed by the interface namespace hierarchy. These would be combined into a single identifier (valid C name). The intention is to get non-ambiguous names, yet avoiding the need of a central registry.
+
+__Conclusion__: accepted
+
+----
+general Topics
+
+!Config System
+''cehteh'' landed the first version of this subsystem and asked for review and testing. Currently it's "work with no progress", but it is basically usable (you can get values, you can set values by environment variables, but you can't persist). It should be augmented on demand, e.g. by adding the support for more value types (value types are a hint how to parse, and link the parsed value to a given C type).
+
+
+!Use of Namespaces
+Currently there is no clear uniform rule regarding namespaces use. ''Joelholdsworth'' places his code within lumiera::gui and below. He points out that external libs like Cairo, GTK, GLib will bring in their hierarchies and all of Lumiera should comply and put anything below a lumiera:: root. On the contrary, ''Ichthyo'' deliberately moved his implementation code away from the central lumiera:: interface hierarchy into shallow namespaces and avoids nesting. He argues that having all code below luimiera:: effectively makes this namespace global or forces it to be void of any function; rather he'd prefer to import every interface explicitly into the implementation namespace. ''Cehteh'' argues that having a global lumiera::, even if empty, would mark a general claim and stand for the uniformity of the project. Generally, there should be some correspondence between folders and namespaces.
+
+No conclusion yet, to be discussed further.
+
+
+!Interface Definition Language
+In his work on the plugin loader, ''Cehteh'' created a first draft how to export an interface, and calls for review. An example can be found in [http://www.lumiera.org/gitweb?p=lumiera/ct;a=blob;f=tests/backend/test-interfaces.c;h=fb1c4d30a34414767a313d24df60e679c96ad2a7;hb=7323114e77348995ccaf03417136aef7ee332776 tests/backend/test-interfaces.c]
+
+An interface is a list of "slots" mapping functions. The interface descriptor is itself realised as interface, an thus can be versioned, extended and specialised. By use of some glue code and macros we create a simple Interface Definition Language
+ * after exporting a header with the C interface, including the types to be used...
+ * LUMIERA_INTERFACE_DECLARE creates an interface description (i.e. the official interface)
+ * implementing such an interface can be done in 3 major ways
+   * LUMIERA_INTERFACE_INSTANCE creates an instance on-the-fly (e.g. for descriptors)
+   * LUMIERA_EXPORT for defining a table of functions/interfaces to export from the core
+   * LUMIERA_PLUGIN for defining an interface table for functions located within a dynlib module (plugin)
+ * besides, with LUMIERA_INTERFACE_INLINE you can create a function on-the-fly while LUMIERA_INTERFACE_MAP maps onto an existing function directly
+
+The plugin loading system cares for mapping the given implementation function to the interface slots. Interfaces from the core need to be registered before they can be used, while for plugins this is done automatically on loading the module. The client code later just calls {{{interface_open()}}} and {{{interface_close()}}} where it doesn't matter anymore if the invoked function is in the core or loaded from an dynlib (plugin); loader, registry and/or plugin-DB shall manage it transparently.
+
+Version numbering starts with 1 and uses minor version numbers for compatible changes and major version numbers for everything that breaks existing asumptions. Version number zero is reserved for experimental work and per definition always the most recent version number.
+
+The system is designed to be very flexible and extensible, but this foundation really needs thorough review.
+
+Joelholdworth expresses some concern regarding the UIDs in octal notation used within the interface description. Cehteh explains they never need to be specified by client code. They are just distinctive IDs and provided for some special case within the plugin loader / serializer. He plans to provide a simple tool which automatically replaces a $LUIDGEN with such a bitstring. The octal notation was chosen, because it is the most compact albeit portable notation possible in C source code.
+
+!!Conclusion
+
+looks good, agreement by all core devs.
+Should be reviewed and investigated in detail to find any hidden problems.
+
+
+!Next meeting
+
+There were some problems with the meeting schedule. Using the first week of month seems to be problematic. We'll try with second wednesday...
+
+The next meeting is scheduled for ''Wednesday Nov 12 2008 19:30 UTC'' at #lumiera
+
+
! 5.June 2008 on #lumiera
 __cehteh__ and __ichthyo__
@@ -2152,23 +2439,26 @@ This distributed wiki might be used instead the pipapo.org wiki, investigate tha
 Wiki works. It is simple to use and just flexible enough to handle the task. I don't go to install any other software for such tasks on my server. While the design progresses I'd propose to move our work into git repositories and eventually phase this wiki pages out anyways. I'd rather like to start out distributed/git right away .. but git gives us only a fine storage layer, for a design process we need some good presentation layer (later when using git and starting the implementation everyones favorite editor serves for that) I have no better ideas yet to solve the presentation problem other than using this wiki (or maybe Bouml).
 
-
+
[<img[draw/LumiLogo.png]]
 
 
+
+
   ''Lumiera'' is the emerging professional non linear video editor for Linux
 
-This is the entry point to several [[TiddlyWiki]]-Pages containing the developer and design documentation.
+
+This is the entry point to several [[TiddlyWiki]]-Pages containing the developer and design documentation. The documentation is split in several parts corresponding to the parts of the application. Those TiddlyWiki pages are self modifying HTML pages; we include them into the GIT source tree. For the future we plan to move the contents of these developer doc wikis into a GIT backed uWiki (still under development as of 10/2008)
 * we maintain (semi-) final design docs in DesignDocumentation
-* Things get often worked out on IRC, see IRC-Transcripts for decisions made there and not yet put into the proper documentation places
+* Things get often worked out on IRC, see IRC-Transcripts for protocols, transcripts and decisions made there
 
 ----
 !Architecture and Subsystems
-to get started, we create design drafts emphasizing different aspects and regions of Lumiera
+&rarr; see the ArchitectureOverview
 
 * Cehteh works on the data backend, see [[this page|backend.html]]
 * Ichthyo focuses mainly on Edit operations and Builder, [[see this separate page|renderengine.html]]
-* Joel started with a GUI implementation based on GTK
+* Joel builds the Lumiera GUI based on GTK
 * Gmerlin is in charge of [[GAVL|http://gmerlin.sourceforge.net/gavl.html]] for processing of video data
 * Some tools which don't fit somewhere else and are used everywhere are put into a [[Support Library|support_library.html]]
 
@@ -3209,10 +3499,11 @@ Typically, one would just write the necessary definitions as they come into one
 Moreover, one could simply list all needed files or break everything down into a hierarchical build. But instead, we use for most objects a helper function (located in {{{admin/scons/Buildhelper.py}}}) called {{{srcSubtree()}}}, which will scan a directory tree and add all {{{'*.c','*.cpp','*.cc'}}} - files as Object nodes to the build process. Besides that, we use the //hierarchical aproach rather reluctant//: at the moment, only the subtree for separate tools and the tests-directory have a separate buildscript. Probably, the subtree for plugins will get one as well at some point in the future.
 
-
+
~~This small Tiddler contains usefull Shortcuts, Info, Links~~
 
-*[[Wiki-Markup|http://tiddlywiki.com/#MainFeatures]]
+*[[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
+*[[Design-Process|http://www.pipapo.org/pipawiki/Lumiera/DesignProcess]]
 *
 ----
 
@@ -4597,82 +4888,11 @@ function addKeyDownHandlers(e) } //}}}
-
+
The Name of the Software driving this Wiki. Is is written completely in ~JavaScript and contained in one single HTML page.
 Thus no server and no network connection is needed. Simply open the file in your browser and save changes locally. As the wiki HTML is located in the Lumiera source tree, all changes will be managed and distributed via [[GIT|GitNotes]]. While doing so, you sometimes will have to merge conflicing changes manually in the HTML source. There is a 'empty.html' in the same folder serving as template for generating new wikis. Please refrain from editing it.
  * see GettingStarted
- * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.com/#MainFeatures]]
-
-
-
-
/%||'''State'''||''Final''||%/
-||'''State'''||''Draft''||
-||'''Date'''||[[Date(2007-06-21T05:12:03Z)]]||
-||'''Proprosed by'''||["Ichthyostega"]||
-
-!time handling
-how to handle time values in the code and which policy to aply to the "current" position
-
-!!Description
-# Representation of time values
-#* we use an uniform time type. Time is time, not frames, samples etc.
-#* all timings in Lumiera are based on integral datatypes
-#* we use a fixed, very fine grid, something of the sort of microseconds
-#* the internal representation is based on a {{{typedef int64_t gavl_time_t}}}
-#* we use a set of library routines and  convenience-methods to
-#** get time in different formats (fractional seconds, frame counts)
-#** calculate with time values (overloaded operators)
-#* time measurement is zero based (of course :-P )
-# Quantizing to a frame index or similar
-#* quantizing/rounding shall happen only once at a defined point in the calculation chain and, if in doubt, be done always as late as possible. 
-#* values needing to be quantized to time (grid) positions are calculated by half-way rounding, but the result should not depend on the actual zero-point of the scale (i.e. {{{floor(0.5+val)}}}, thus quant(0.5) yields 1, quant(0.49) yields 0, quant(-0.5) yields 0 )
-# Position of frames[>img[draw/FramePositions1.png]]
-#* frame numbers are zero based and Frame 0 starts at time=0 (or whatever the nominal start time is)
-#* each frame starts when the locator hits its lower border (inclusively) and ends when the locator is on its upper border (exclusively)
-#** when the locator snaps to frames this means it can be placed on the start positions of the frames solely
-#** when the locator is placed on such a start position, this means //always// displaying the frame starting at this position, irrespective of playback direction.
-# Current position for keyframe nodes and automation[>img[draw/FramePositions2.png]]
-#* when parameter values for plugins/automation need to be retrieved on a per frame base (which normally is the case), for each frame there is a well defined __//point of evalutation//__ time position, irrespective of the playback direction
-#* there is no single best choice where to put this "POE", thus we provide a switch
-#** //point of evalutation// of the automation is in the middle of the timespan covered by a frame
-#** //point of evalutation// is on the lower bound of each frame
-#** maybe additional position or fraction (?)
-#* moreover, we provide an option to snap the keyframe nodes to the //point of evalutation// within each frame or to be able to move them arbitrarily
-#* when keyframes are set by tweaking of parameters, they are located at the //point of evalutation// position.
-
-
-!!!!Tasks
-* figure out what has to be done when switching the "current position" policy on a existing project
-
-
-!!!Alternatives
-Leave everything as in Cinelerra2, i.e. show frames after the locator has passed over them, behave differnt when playing backwards and set the keyframes on the position of the locator but use them on the frame actually to be shown (which differs according to the playback direction but is always "one off").
-
-Why not? because it makes frame-precise working with keyframes a real pain and even creates contradictory situations when you
-switch back and forward while tweaking.
-
-Similar for the issues with quantized values. At first sight, e.g. directly using the frame numbers as coordinates (as Cinelerra does) seems to be clever, but on the long run we get lots of case distinctions scattered all over the code. Thus better use one uniform scheme and work with precise time values as long as possible and only quantize for rendering a given frame.
-
-
-!!Rationale
-The intention is to make time handling and calculations as uniform and "rational" as possible. We try to stick to the precise mathematical values and let the calculations just yield an result in an uniform manner, instead of sticking to "simple" values like frame counts or even a session-wide frame rate
-# time and interval calculations are tricky. Solve this question once and be done.
-# rounding is always dangerous, rounded values are not the more "clean" values. The floor-rounding rule is chosen, because the length of an interval after quantizion should not depend on the position in relation to the zero point. The usual mathematical rounding behaves "symmetrical" to the zero point, which could yield a different length after quantizion if an interval contains the zero point
-# this is based on the analoy with real film running through a film projector (or the usual fencepost problem)
-# while using the actual position of the locator as the "current" position for keyframes seems more natural at first, it crates problems when mixing footage with different framerate or when using a low-framerate proxy footage
-
-
-
-!Comments
-This is the summary of a discussion cehteh, Plouj and ichthyo just had on irc.
- -- ["Ichthyostega"] //2007-06-21T05:12:03Z//
-
-We use Gavl now
- -- ["ct"] //2008-03-05T16:19:22Z//
-
-I've tidied up this old design proposal, we could make it final now. I've changed the rounding rule, please check if it's OK. In the original proposal, we  wanted to use the common mathematical rounding rule, i.e. round(-x) = - round(x) . I changed this, because of the danger of interval lengts or alignment to "jump" dependant on the position in relation to the time zero point.
- -- ["Ichthyostega"] //2008-10-04T22:47:54Z//
-
+ * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
 
From 9752de310229f5f4c2ddb2eecd31c59eb5acd4ec Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 15 Oct 2008 22:05:22 +0200 Subject: [PATCH 63/66] Some prelimary TODO's for filedescriptor.c to be fixed later --- src/backend/filedescriptor.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/backend/filedescriptor.c b/src/backend/filedescriptor.c index 4daa04f3e..5f6bb4f13 100644 --- a/src/backend/filedescriptor.c +++ b/src/backend/filedescriptor.c @@ -42,7 +42,7 @@ NOBUG_DEFINE_FLAG_PARENT (filedescriptor, file_all); This registry stores all acquired filedescriptors for lookup, they will be freed when not referenced anymore. */ static PSplay registry = NULL; -static lumiera_mutex registry_mutex = {PTHREAD_MUTEX_INITIALIZER}; +static lumiera_mutex registry_mutex = {PTHREAD_MUTEX_INITIALIZER NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; static int @@ -96,7 +96,9 @@ lumiera_filedescriptor_registry_init (void) if (!registry) LUMIERA_DIE (NO_MEMORY); - RESOURCE_HANDLE_INIT (registry_mutex.rh); + TODO ("LumieraMutex lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag);"); + + // RESOURCE_HANDLE_INIT (registry_mutex.rh); RESOURCE_ANNOUNCE (filedescriptor, "mutex", "filedescriptor registry", ®istry, registry_mutex.rh); } @@ -108,6 +110,8 @@ lumiera_filedescriptor_registry_destroy (void) RESOURCE_FORGET (filedescriptor, registry_mutex.rh); + TODO ("LumieraMutex lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag);"); + if (registry) psplay_destroy (registry); registry = NULL; From 26c212b2928b52b22d3f00b41025048be3a79fb5 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 15 Oct 2008 22:18:17 +0200 Subject: [PATCH 64/66] make the old plugin stuff barely compileable, disable tests, fix later --- src/backend/plugin.c | 5 +++-- tests/20plugin.tests | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/plugin.c b/src/backend/plugin.c index 44cf6abc5..f6ecd7649 100644 --- a/src/backend/plugin.c +++ b/src/backend/plugin.c @@ -27,8 +27,9 @@ #include #include -#include "plugin.h" -#include "safeclib.h" +#include "lib/safeclib.h" + +#include "backend/plugin.h" /** * @file diff --git a/tests/20plugin.tests b/tests/20plugin.tests index 6e8cd96de..d1d9fba2f 100644 --- a/tests/20plugin.tests +++ b/tests/20plugin.tests @@ -1,6 +1,6 @@ TESTING "test plugin example code" ./test-plugin -TEST "C plugin example" C < Date: Wed, 15 Oct 2008 22:53:55 +0200 Subject: [PATCH 65/66] Fix: old plugin.c|.h must not be compiled and linked --- src/backend/Makefile.am | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index cf5fe1897..da39e71c6 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -23,7 +23,6 @@ liblumibackend_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wextra -Wall -Werror liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/mediaaccessfacade.cpp \ $(liblumibackend_a_srcdir)/backend.c \ - $(liblumibackend_a_srcdir)/plugin.c \ $(liblumibackend_a_srcdir)/file.c \ $(liblumibackend_a_srcdir)/filehandle.c \ $(liblumibackend_a_srcdir)/filedescriptor.c \ @@ -41,7 +40,6 @@ liblumibackend_a_SOURCES = \ noinst_HEADERS += \ $(liblumibackend_a_srcdir)/mediaaccessfacade.cpp \ $(liblumibackend_a_srcdir)/backend.h \ - $(liblumibackend_a_srcdir)/plugin.h \ $(liblumibackend_a_srcdir)/file.h \ $(liblumibackend_a_srcdir)/filehandle.h \ $(liblumibackend_a_srcdir)/filedescriptor.h \ From 55d592f119ff885754ae8b8ee4cad16fb45ae12d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 16 Oct 2008 02:07:32 +0200 Subject: [PATCH 66/66] adapt for the plugin-test now being in tests/backend --- tests/15safeclib.tests | 7 +++++-- tests/SConscript | 22 ++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/tests/15safeclib.tests b/tests/15safeclib.tests index 560c0ee0a..a329bda83 100644 --- a/tests/15safeclib.tests +++ b/tests/15safeclib.tests @@ -9,9 +9,12 @@ TEST "Allocating some memory" allocation1024 <