From 51b3f0defae55e7067c0850040e6cc79ae5b9f91 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 22 May 2011 00:50:23 +0200 Subject: [PATCH 001/296] start a page to collect technical notes about Scheduler and Jobs (Backend) Signed-off-by: Ichthyostega --- doc/technical/backend/index.txt | 3 ++- doc/technical/backend/scheduler.txt | 40 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 doc/technical/backend/scheduler.txt diff --git a/doc/technical/backend/index.txt b/doc/technical/backend/index.txt index 58fd1a5e9..4e7c2bda9 100644 --- a/doc/technical/backend/index.txt +++ b/doc/technical/backend/index.txt @@ -1,8 +1,9 @@ Technical Documentation: Backend ================================ -Eventually, this will have technical documentation for the Backend. +Here we collect bits of technical documentation for the Backend. For now, we have: * link:ConfigLoader.html[Config Loader brainstorming from 2008] + * link:scheduler.html[Scheduler and Jobs] diff --git a/doc/technical/backend/scheduler.txt b/doc/technical/backend/scheduler.txt new file mode 100644 index 000000000..f62da23dd --- /dev/null +++ b/doc/technical/backend/scheduler.txt @@ -0,0 +1,40 @@ +Scheduler and Job handling +========================== + +The purpose of the _Scheduler_ is to run small self contained _Jobs_ +ordered by priority and observing specific timing constraints. + +Scheduler implementation ideas +------------------------------ + +Use multiple priority queues + +- background work +- foreground high-priority +- soft-realtime actions + +About Jobs +---------- +A job is a closure to run a small and limited action or operation, which +in itself _should not block_. Job may depend on each other and on resources +to be provided. A job may be conained in multiple queues and may be marked +as _canceled_ -- in which case the job function will never run and the job +will be discarded on occasion. + +Job States +~~~~~~~~~~ + +[source,C] +-------------- +enum job_state +{ + done, // already done, nothing to do + running, // job is running + waiting, // waits for some resource (annother job) + rejected, // sorry, cant do that dave, time will run out + expired, // time expired + aborted // got aborted +} +-------------- + + From c6f4f9bf25035cfe0ac37e5b4354e99b4c62bf50 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 22 May 2011 05:45:59 +0200 Subject: [PATCH 002/296] create pages and sections for the design of output handling and player subsystem Signed-off-by: Ichthyostega --- doc/design/architecture/playRender.txt | 20 ++++++------- doc/design/engine/OutputHandling.txt | 15 ++++++++++ doc/design/engine/index.txt | 3 +- doc/user/intro/intro.txt | 39 ++++++++++++++------------ 4 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 doc/design/engine/OutputHandling.txt diff --git a/doc/design/architecture/playRender.txt b/doc/design/architecture/playRender.txt index afcf2b61f..2d76f716e 100644 --- a/doc/design/architecture/playRender.txt +++ b/doc/design/architecture/playRender.txt @@ -70,8 +70,8 @@ proceeding along a timeline). Reconfiguration ^^^^^^^^^^^^^^^ -Some of these operation modes need to be prepared to an unpredictable live reconfiguration, -driven by user interactions: +Some of these operation modes need to be prepared to encounter an unpredictable live +reconfiguration, driven by user interactions: - any part of background rendering can be invalidated and restarted, while other parts should be re-integrated, possibly with adjusted position @@ -108,7 +108,7 @@ playback location, and it can be hooked up with a play-control GUI widget Each play-controller in turn gets associated with several *play/render-processes*, one for each independent media stream (channel) to be produced. Of course this -isn't an operating system process; rather, ach such process is a compound of entries +isn't an operating system process; rather, each such process is a compound of entries in a registration table, which serve the purpose of tying several other services together, which we initiate and use in order to make that render process happen. Most notably, we'll use the services of the actual engine, which provides us with kind of @@ -126,7 +126,7 @@ Viewer and Output connection ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Creating a player instance binds together three partners: a _timeline_, a _viewer_ and _the engine_. While the timeline provides the content to play, the _viewer connection_ -is crutial for working out the actual output sink(s) and thus the output format to use. +is crucial for working out the actual output sink(s) and thus the output format to use. Thus, a viewer connection is prerequisite for creating a player instance. Viewer connections exist as associations in the session/model -- as entities separate @@ -135,13 +135,13 @@ such a connection is (still) missing, building a player instance recurs to the s to get a suitable viewer _allocated_. The viewer connection can't be broken during the lifetime of that player instance (or putting it the other way: breaking that viewer connection, e.g. by forcing a different connection or by shutting down the viewer, -immediately terminates the player. This detaching works synchroneously, i.e. it -blocks untlil all the allocated _output slots_ could be released. +immediately terminates the player. This detaching works synchronously, i.e. it +blocks until all the allocated _output slots_ could be released. Live switching ^^^^^^^^^^^^^^ While the viewer connection can be treated as fixed during the lifespan of a player -instance, several life switching and reconfiguration operations might happen anytime: +instance, several life switching and reconfiguration operations might happen any time: The _model port_ (place where data is retrieved from calculation), the output characteristics (framerate, direction) and the delivery goals (playback position, loop playing, scrubbing) all may be changed during playback -- we need a way for the player to ``cancel'' and @@ -164,7 +164,7 @@ the quantisation, which leaves us with just a few possible junction points where to place quantisation: The backend, the GUI, the player, the session. - putting it into the backend seems to be the most reasonable at first sight: - We can ``do away'' with nasty things soon, especially if they are technicallities, + We can ``do away'' with nasty things soon, especially if they are technicalities, ``get a clean state soon'' -- and hasn't frame quantisation something to do with media data, which is handled in the backend? + @@ -174,7 +174,7 @@ amount of degraded information flows throughout the whole system; thus the general rule to do it as late as possible. Uncrippled information is enablement. And last but not least: the frame quantisation is connected to the _output_ format -- and the backend is likely within the whole -application the subsytem most remote and unaware of output requirements. +application the subsystem most remote and unaware of output requirements. - rounding/quantising in the GUI is extremely common within media applications; unfortunately there seems to be not a single rational argument supporting that habit. @@ -184,7 +184,7 @@ Which leaves us with the player and the session. Both positions could arguably be supported. Here, a more careful consideration shows, that the ``act of frame rounding'' can be decomposed: into the _act of quantisation_ and the _frame grid:. Basically its the session which has the ability -to form the *frame grid*, but it is lacking crutial information about +to form the *frame grid*, but it is lacking crucial information about the output. Only when connecting both -- which is the essence of the player -- frame quantisation can actually be performed. Thus, the player is the natural location to perform that quantisation operation. diff --git a/doc/design/engine/OutputHandling.txt b/doc/design/engine/OutputHandling.txt new file mode 100644 index 000000000..fa3d5e268 --- /dev/null +++ b/doc/design/engine/OutputHandling.txt @@ -0,0 +1,15 @@ +Design: Output Handling +======================= +:Date: June 2011 +:Author: Ichthyostega + +//Menu: label Output handling + +Some ideas.... + +- abstract away the actual technology used for output +- have generic *output designations* and translate them into an *output slot* +- the OutputSlot interface can be designed to match the requirements of the Engine +- assume a mechanism to handle timeouts, glitches and skips within each concrete OutputSlot implementation + + diff --git a/doc/design/engine/index.txt b/doc/design/engine/index.txt index 7d9b5ea39..0852b6bac 100644 --- a/doc/design/engine/index.txt +++ b/doc/design/engine/index.txt @@ -1,5 +1,6 @@ Design Documents: Renderengine ============================== -Eventually, this will have design documentation for the Engine. +This section contains design documents regarding the overall workings of the Render Engine, +and the handling of output generation and output connections. diff --git a/doc/user/intro/intro.txt b/doc/user/intro/intro.txt index 0e32a454b..5617dd44a 100644 --- a/doc/user/intro/intro.txt +++ b/doc/user/intro/intro.txt @@ -1,6 +1,7 @@ Lumiera (as seen) from Outer Space ================================== :Author: Christian Thäter ct@pipapo.org +:Author: Hermann Voßeler Ichthyostega@web.de :Date: Summer 2010 @@ -18,13 +19,10 @@ they get to work with. It is aimed for workflow designers any anyone who wants to know how the program works in general. ****************************************************************************** - -About this Document -------------------- - // all things starting with '//' are asciidoc comments and drafts/notes while // working on this document +.About this Document This document is meant to be read electronically, it contains a lot hyper-links between explanations denoted by an arrow ->. Lumiera is still in development, we describe here planned features without explicitly tagging them; @@ -38,8 +36,13 @@ Vision // objective and goals of the project -Lumiera claims to be `professional', this is quite a vague term and needs -some explanation what it means to us: +Lumiera claims to be a _professional non-linear video editor_. To start with, we should +point out that ``professional'' does not necessarily mean ``commercial'' or ``industrial''. +It's more of an attitude or mindset -- doing work seriously, and to be subject to any +kind of wider goal, demand, or purpose. When it comes to editing film, this might be +artistry, a narration or meaning to convey, a political message or something to show +to your audience. Anyhow, for the tools, the editing software used to this end, +we can identify several properties and requirements, to be labeled ``professional'': Reliability:: Whatever happens, your work must be safe, protected against software @@ -47,34 +50,34 @@ some explanation what it means to us: never crash, in practice even crashes or power outages should not result in lost work. - Productivity:: + Quality:: + If you work with high quality, cinema grade digital video material you + want to be sure that you can deliver crisp quality without compromise, + throughout the whole workflow to your final product. All rendering + must be reproducible to the bit. + + Performance and Productivity:: Professionals want to get things done, in time, but optionally with control over every aspect. Balancing these goals should be the central concern for workflow design and usability. - Quality:: - If you work with high quality, cinema grade digital video material you - want to be sure that you can deliver this crisp quality without - compromise throughout you workflow to your final product. All rendering - must be reproducible to the bit. - - Scalability:: + Scalability and Adaptability:: Projects and budgets differ, hardware advances, Lumiera must scale in different dimensions and use the available resources as best as it - can. From small Laptops to multicore Computers and Renderfarms. + can. From small Laptops to multi core computers and Renderfarms. - Future Proofness:: + Durability:: Soft and Hardware advances at a fast pace. We must not lock into the current state of technology but being flexible to extend the System without breaking compatibility. Projects you create nowadays with Lumiera should be usable in foreseeable future, at least there needs to be a guaranteed upgrade path. -< + Fundamental Forces ------------------ -// the basic ideas which drive the lumiera design +// the basic ideas which drive the Lumiera design The Lumiera design is guided by a small number of basic principles. Keeping these in mind will help to understand how actually more interesting things can From 64cf3c9d34715f1fb915714f057f2fb25f78cf90 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 25 Aug 2011 03:03:43 +0200 Subject: [PATCH 003/296] update GDB-pretty-printer HOWTO Eclipse now supports structured display of STL containers --- doc/technical/howto/DebugGdbPretty.txt | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/doc/technical/howto/DebugGdbPretty.txt b/doc/technical/howto/DebugGdbPretty.txt index b46c70a72..e1a2c6f97 100644 --- a/doc/technical/howto/DebugGdbPretty.txt +++ b/doc/technical/howto/DebugGdbPretty.txt @@ -5,16 +5,24 @@ Since some time, GDB supports Python written extension modules, especially for pretty printing of data structures. A selection of pre-defined pretty printers for STL containers is part of the standard distribution. The graphical debugger frontend provided by the Eclipse CDT automatically uses this debugger provided presentation -to show the value of variables in the detail pane of the variables view, while the -individual variable entries always show the expandable structure view. +to show the value of variables in the detail pane of the variables view. The most recent +version of CDT (Version 8 for Eclipe 3.7 »Indigo«) is even able to populate the structure view +based on the python pretty printer's output, which is a big step ahead towards easy debugging +of more elaborate data structures based on STL containers. + Installation notes ------------------ -This feature requires an python enabled GDB (>6.8.50). Debian/Lenny isn't enough, -but compiling the GDB package from Debian/Squeeze (GDB-7.1) is straightforward. -Moreover, you need to check out and install the python pretty-printers and -you need to activate them through your +~/.gdbinit+ +This feature requires an python enabled GDB (>6.8.50). Actually, even the most recent stable +GDB (Version 7.2) is recommended, because it contains some relevant bugfixes. Debian users +might want to backport the the GDB package from Debian/Wheezy (GDB-7.2). +Moreover, you need to check out and install a recent version of the python pretty-printers +from the GNU Subversion repository: + +* 'svn checkout' + http://gcc.gnu.org/viewcvs/trunk/libstdc%2B%2B-v3/python/[svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python stlPrettyPrinter] +* you need to activate them explicitly through your +~/.gdbinit [source,python] ---- @@ -61,4 +69,8 @@ When selecting the string or the vector in the Eclipse variables view (or when issuing "print str" at the GDB prompt), the GDB python pretty-printer should be activated. +NOTE: to get the full support in _Eclipse Indigo + CDT-8_, you need to activate + the setting ``enable pretty printers in variable/expression tree'', which + can be accessed as Window/Preferences > C++ / debug / GDB + From 2547580d31f4aba80abd363beca3f720d890e0dc Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Thu, 15 Sep 2011 02:32:31 +0200 Subject: [PATCH 004/296] Fix typo in website menu --- doc/devel/report.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/devel/report.txt b/doc/devel/report.txt index 7734b66dc..deec2fd15 100644 --- a/doc/devel/report.txt +++ b/doc/devel/report.txt @@ -1,7 +1,7 @@ Statistics and Reports ====================== -//Menu: label Statisticts +//Menu: label Statistics ++++++++++++++++++++++++++++++++++++++ From e34bb5ead147fc31815298a67685a6f75e393293 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Thu, 15 Sep 2011 04:35:08 +0200 Subject: [PATCH 005/296] Fix autotool build again, still fails on setup.ini --- src/backend/Makefile.am | 2 +- src/gui/Makefile.am | 4 ++-- src/lib/Makefile.am | 12 ++++++++++-- tests/components/Makefile.am | 1 - 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 7540f1445..854d44f26 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -31,7 +31,7 @@ liblumierabackend_la_SOURCES = \ $(liblumierabackend_la_srcdir)/filehandle.c \ $(liblumierabackend_la_srcdir)/filehandlecache.c \ $(liblumierabackend_la_srcdir)/fileheader.c \ - $(liblumierabackend_la_srcdir)/mediaaccessfacade.cpp \ + $(liblumierabackend_la_srcdir)/media-access-facade.cpp \ $(liblumierabackend_la_srcdir)/mmap.c \ $(liblumierabackend_la_srcdir)/mmapcache.c \ $(liblumierabackend_la_srcdir)/mmapings.c \ diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index 095baf82c..679c6be06 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -112,9 +112,9 @@ gtk_gui_la_SOURCES = \ $(lumigui_srcdir)/widgets/timecode-widget.hpp \ $(lumigui_srcdir)/widgets/timeline-widget.cpp \ $(lumigui_srcdir)/widgets/timeline-widget.hpp \ - $(lumigui_srcdir)/widgets/timeline/basic-draw-strategy.cpp \ $(lumigui_srcdir)/widgets/timeline/basic-draw-strategy.hpp \ - $(lumigui_srcdir)/widgets/timeline/draw-strategy.hpp \ + $(lumigui_srcdir)/widgets/timeline/draw-strategy.cpp \ + $(lumigui_srcdir)/widgets/timeline/draw-strategy.hpp \ $(lumigui_srcdir)/widgets/timeline/timeline-arrow-tool.cpp \ $(lumigui_srcdir)/widgets/timeline/timeline-arrow-tool.hpp \ $(lumigui_srcdir)/widgets/timeline/timeline-body.cpp \ diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index a44b47423..003773a51 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -63,6 +63,7 @@ liblumiera_la_SOURCES = \ $(liblumiera_la_srcdir)/test/testoption.cpp \ $(liblumiera_la_srcdir)/time.cpp \ $(liblumiera_la_srcdir)/time/lumitime.cpp \ + $(liblumiera_la_srcdir)/time/mutation.cpp \ $(liblumiera_la_srcdir)/time/quantiser.cpp \ $(liblumiera_la_srcdir)/time/timecode.cpp \ $(liblumiera_la_srcdir)/tmpbuf.c \ @@ -83,7 +84,6 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/lifecycleregistry.hpp \ $(liblumiera_la_srcdir)/lockerror.h \ $(liblumiera_la_srcdir)/luid.h \ - $(liblumiera_la_srcdir)/lumitime.hpp \ $(liblumiera_la_srcdir)/meta/configflags.hpp \ $(liblumiera_la_srcdir)/meta/generator.hpp \ $(liblumiera_la_srcdir)/meta/typelist-util.hpp \ @@ -110,12 +110,20 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/singleton.hpp \ $(liblumiera_la_srcdir)/sync-classlock.hpp \ $(liblumiera_la_srcdir)/sync.hpp \ - $(liblumiera_la_srcdir)/test/mockinjector.hpp \ $(liblumiera_la_srcdir)/test/run.hpp \ $(liblumiera_la_srcdir)/test/suite.hpp \ $(liblumiera_la_srcdir)/test/test-helper.hpp \ $(liblumiera_la_srcdir)/test/testoption.hpp \ $(liblumiera_la_srcdir)/time.h \ + $(liblumiera_la_srcdir)/time/diagnostics.hpp \ + $(liblumiera_la_srcdir)/time/digxel.hpp \ + $(liblumiera_la_srcdir)/time/display.hpp \ + $(liblumiera_la_srcdir)/time/formats.hpp \ + $(liblumiera_la_srcdir)/time/grid.hpp \ + $(liblumiera_la_srcdir)/time/mutation.hpp \ + $(liblumiera_la_srcdir)/time/quantiser.hpp \ + $(liblumiera_la_srcdir)/time/timecode.hpp \ + $(liblumiera_la_srcdir)/time/timevalue.hpp \ $(liblumiera_la_srcdir)/tmpbuf.h \ $(liblumiera_la_srcdir)/tree.hpp \ $(liblumiera_la_srcdir)/util.hpp \ diff --git a/tests/components/Makefile.am b/tests/components/Makefile.am index 5da67c332..6a21d98bd 100644 --- a/tests/components/Makefile.am +++ b/tests/components/Makefile.am @@ -111,7 +111,6 @@ test_components_SOURCES = \ noinst_HEADERS += \ - $(testcomponents_srcdir)/backend/mediaaccessmock.hpp \ $(testcomponents_srcdir)/proc/asset/asset-diagnostics.hpp \ $(testcomponents_srcdir)/proc/asset/testasset.hpp \ $(testcomponents_srcdir)/proc/asset/testclipasset.hpp \ From 14bc7d1fd4e467172fd7b1ebdf22ff4c9546c447 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 25 Sep 2011 19:16:13 +0200 Subject: [PATCH 006/296] fix regression in test definition random time values are really random. Doh! --- tests/45controller.tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/45controller.tests b/tests/45controller.tests index 81a23996a..178e0fc7b 100644 --- a/tests/45controller.tests +++ b/tests/45controller.tests @@ -38,7 +38,7 @@ END TEST "build argument accepting function" ArgumentTupleAccept_test < Date: Sun, 25 Sep 2011 19:10:57 +0200 Subject: [PATCH 007/296] Clean-up: change some long standing TODOs into tickets makes the test logs way more readable Believe me: no one will ever notice a "TODO" entry in the logs, when it showed up for more than some months. Thus I've created some new tickets, mostly tagged as "QA" and placed the ticket number at the corresponding locations in the source --- src/backend/backend.c | 5 +++-- src/backend/threadpool.c | 9 +++++--- src/backend/threads.c | 4 ++-- .../{config_lookup.c => config-lookup.c} | 9 +++++--- .../{config_lookup.h => config-lookup.h} | 2 +- src/common/config.h | 2 +- src/common/configentry.c | 4 ++-- src/common/configitem.c | 21 ++++++++++--------- src/common/configitem.h | 2 +- src/lib/{hash_fnv.c => hash-fnv.c} | 2 +- src/lib/{hash_fnv.h => hash-fnv.h} | 2 +- src/lib/test/suite.cpp | 1 + src/proc/asset/media.cpp | 4 ++-- src/proc/assetmanager.cpp | 4 ++-- .../mobject/session/sess-manager-impl.cpp | 2 +- 15 files changed, 41 insertions(+), 32 deletions(-) rename src/common/{config_lookup.c => config-lookup.c} (93%) rename src/common/{config_lookup.h => config-lookup.h} (98%) rename src/lib/{hash_fnv.c => hash-fnv.c} (99%) rename src/lib/{hash_fnv.h => hash-fnv.h} (99%) diff --git a/src/backend/backend.c b/src/backend/backend.c index 0f2c62b8a..694050c9e 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -102,7 +102,8 @@ lumiera_backend_init (void) lumiera_backend_pagesize = sysconf(_SC_PAGESIZE); - TODO ("add config options to override following defaults"); + /////////////////////////////////////////////////////////////////////TICKET #838 add config options to override following defaults" + const char* filehandles = lumiera_tmpbuf_snprintf (SIZE_MAX, "backend.file.max_handles = %d", @@ -168,7 +169,7 @@ lumiera_backend_mpool_purge (enum lumiera_resource_try itr, void* data, void* co (void) context; (void) data; (void) itr; - TODO("mpool_purge ((MPool) data)"); + ///////////////////////////////////////////////////////////TICKET #837 actually implement mpool purging return LUMIERA_RESOURCE_NONE; } diff --git a/src/backend/threadpool.c b/src/backend/threadpool.c index b0ac7c0b3..ed035c209 100644 --- a/src/backend/threadpool.c +++ b/src/backend/threadpool.c @@ -76,8 +76,9 @@ lumiera_threadpool_destroy(void) { LUMIERA_CONDITION_SECTION (cond_sync, &threadpool.pool[i].sync) { - TODO ("check threads deadlines, kill them when they are stalled"); - TODO ("for threads without deadline use a timeout from config system, 500ms or so by default"); + //////////////////////////////////////////TICKET #843 check threads deadlines, kill them when they are stalled" + //////////////////////////////////////////TICKET #843 for threads without deadline use a timeout from config system, 500ms or so by default + LUMIERA_CONDITION_WAIT(llist_is_empty (&threadpool.pool[i].working_list)); } } @@ -136,7 +137,9 @@ lumiera_threadpool_acquire_thread (enum lumiera_thread_class kind, llist_insert_head (&threadpool.pool[kind].working_list, &ret->node); ENSURE (ret, "did not create a valid thread"); - TODO ("no error handing, let the resourcecollector do it, no need when returning the thread"); + //////////////////////////////////////////////////////////////////////TICKET #844 no error must be pending here + //////////////////////////////////////////////////////////////////////TICKET #844 let the resourcecollector do it, no need when returning the thread + LUMIERA_CONDITION_WAIT (!llist_is_empty (&threadpool.pool[kind].idle_list)); } // use an existing thread, pick the first one diff --git a/src/backend/threads.c b/src/backend/threads.c index 1261fe134..3ed36be84 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -120,7 +120,7 @@ thread_loop (void* thread) INFO (threads, "Thread Shutdown"); } - TODO ("no error must be pending here, else do app shutdown"); + //////////////////////////////////////////////////////////////////////TICKET #844 no error must be pending here, else do app shutdown return 0; } @@ -322,7 +322,7 @@ lumiera_thread_sync (void) self->state = LUMIERA_THREADSTATE_SYNCING; lumiera_condition_signal (&self->signal, &NOBUG_FLAG(threads), NOBUG_CONTEXT); - TODO("error handing, maybe timed mutex (using the threads heartbeat timeout, shortly before timeout)"); + //////////////////////////////////////////TICKET #843 error handing, maybe timed mutex (using the threads heartbeat timeout, shortly before timeout) while (self->state == LUMIERA_THREADSTATE_SYNCING) { lumiera_condition_wait (&self->signal, &NOBUG_FLAG(threads), self->rh, NOBUG_CONTEXT); diff --git a/src/common/config_lookup.c b/src/common/config-lookup.c similarity index 93% rename from src/common/config_lookup.c rename to src/common/config-lookup.c index 0c0a776a4..3daf24f10 100644 --- a/src/common/config_lookup.c +++ b/src/common/config-lookup.c @@ -23,7 +23,7 @@ #include "lib/safeclib.h" #include "lib/tmpbuf.h" -#include "common/config_lookup.h" +#include "common/config-lookup.h" #include "common/config.h" /* we only use one fatal error for now, when allocation in the config system fail, something else is pretty wrong */ @@ -76,7 +76,8 @@ lumiera_config_lookup_insert (LumieraConfigLookup self, LumieraConfigitem item) REQUIRE (item->key); REQUIRE (item->key_size); - FIXME ("implement section prefix/suffix for the key"); + ////////////////////////////////////////TICKET #839 implement section prefix/suffix for the key" + const char* key = lumiera_tmpbuf_strcat3 (NULL, 0, item->key, item->key_size, NULL, 0); LumieraConfigLookupentry entry = (LumieraConfigLookupentry)psplay_find (&self->tree, key, 100); @@ -101,7 +102,9 @@ lumiera_config_lookup_insert_default (LumieraConfigLookup self, LumieraConfigite LumieraConfigLookupentry entry = (LumieraConfigLookupentry)psplay_find (&self->tree, key, 100); if (!entry) entry = (LumieraConfigLookupentry)psplay_insert (&self->tree, &lumiera_config_lookupentry_new (key)->node, 100); - TODO ("else check that no 'default' item already exists, that is, the tail element's parent points to the 'defaults' in config"); + + ////////////////////////////////////////TICKET #839 check that no 'default' item already exists when inserting a default + ////////////////////////////////////////TICKET #839 ...that is, the tail element's parent points to the 'defaults' in config llist_insert_tail (&entry->configitems, &item->lookup); return entry; diff --git a/src/common/config_lookup.h b/src/common/config-lookup.h similarity index 98% rename from src/common/config_lookup.h rename to src/common/config-lookup.h index eadf96b39..29f5d6d18 100644 --- a/src/common/config_lookup.h +++ b/src/common/config-lookup.h @@ -1,5 +1,5 @@ /* - config_lookup.h - Lookup functions for the config subsystem + CONFIG-LOOKUP.h - Lookup functions for the config subsystem Copyright (C) Lumiera.org 2008, Christian Thaeter diff --git a/src/common/config.h b/src/common/config.h index be8b47171..48ac51a4b 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -51,7 +51,7 @@ LUMIERA_ERROR_DECLARE (CONFIG_NO_ENTRY); //TODO: Lumiera header includes// -#include "common/config_lookup.h" +#include "common/config-lookup.h" #include "common/configitem.h" //TODO: System includes// diff --git a/src/common/configentry.c b/src/common/configentry.c index ef02f5149..5ee5f35ae 100644 --- a/src/common/configentry.c +++ b/src/common/configentry.c @@ -43,7 +43,7 @@ lumiera_configentry_new (LumieraConfigitem tmp) LumieraConfigentry self = lumiera_malloc (sizeof (*self)); lumiera_configitem_move ((LumieraConfigitem)self, tmp); - TODO ("initialize other stuff here (lookup, parent, ...)"); + //////////////////////////////////////////////////////////////////TICKET #839 initialise other stuff here (lookup, parent, ...) return (LumieraConfigitem)self; } @@ -52,7 +52,7 @@ lumiera_configentry_new (LumieraConfigitem tmp) LumieraConfigitem lumiera_configentry_destroy (LumieraConfigitem self) { - TODO ("cleanup other stuff here (lookup, parent, ...)"); + //////////////////////////////////////////////////////////////////TICKET #839 cleanup other stuff here (lookup, parent, ...) return self; } diff --git a/src/common/configitem.c b/src/common/configitem.c index 1a07ee820..de290e16a 100644 --- a/src/common/configitem.c +++ b/src/common/configitem.c @@ -20,6 +20,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/** @file configitem.c + ** create a configitem out of a single line. + ** + */ + + #include "include/logging.h" #include "lib/llist.h" #include "lib/safeclib.h" @@ -43,12 +50,6 @@ static LumieraConfigitem parse_configentry (LumieraConfigitem self, char* itr); #include -/** - * @file - * create a configitem out of a single line. - * - */ - LumieraConfigitem lumiera_configitem_init (LumieraConfigitem self) @@ -176,11 +177,11 @@ lumiera_configitem_parse (LumieraConfigitem self, const char* line) lumiera_free (self->line); self->line = lumiera_strndup (line, SIZE_MAX); - FIXME ("MOCKUP START"); + /////////////////////////TODO do a real startup here char* itr = self->line; - /* skip leading whitespaces */ + /* skip leading whitespace */ while (*itr && isspace (*itr)) itr++; @@ -215,7 +216,7 @@ parse_directive (LumieraConfigitem self, char* itr) /* itr points now to @ */ self->key = itr; - /* check whether there are illegal whitespaces after @ */ + /* check whether there are illegal whitespace after @ */ itr++; if (*itr && !isspace(*itr)) { @@ -224,7 +225,7 @@ parse_directive (LumieraConfigitem self, char* itr) itr += self->key_size; - /* we need a key with a length greather than zero and */ + /* we need a key with a length greater than zero and */ /* either end of line or whitespace after key */ if ( self->key_size && ( !*itr || (*itr && isspace(*itr)) )) diff --git a/src/common/configitem.h b/src/common/configitem.h index 49ae8b62d..92d12a6c2 100644 --- a/src/common/configitem.h +++ b/src/common/configitem.h @@ -33,7 +33,7 @@ typedef lumiera_configitem* LumieraConfigitem; struct lumiera_configitem_vtable; //TODO: Lumiera header includes// -#include "common/config_lookup.h" +#include "common/config-lookup.h" //TODO: System includes// diff --git a/src/lib/hash_fnv.c b/src/lib/hash-fnv.c similarity index 99% rename from src/lib/hash_fnv.c rename to src/lib/hash-fnv.c index c0cdc8095..1842b3819 100644 --- a/src/lib/hash_fnv.c +++ b/src/lib/hash-fnv.c @@ -39,7 +39,7 @@ slightly modified and adapted to C99; no copyright applies: */ -#include "hash_fnv.h" +#include "lib/hash-fnv.h" #include diff --git a/src/lib/hash_fnv.h b/src/lib/hash-fnv.h similarity index 99% rename from src/lib/hash_fnv.h rename to src/lib/hash-fnv.h index aa8db900a..95d5c5aeb 100644 --- a/src/lib/hash_fnv.h +++ b/src/lib/hash-fnv.h @@ -1,5 +1,5 @@ /* - hash_fnv.c - FNV hash functions + HASH-FNV.h - FNV hash functions Copyright (C) 2010, 2011, Christian Thaeter diff --git a/src/lib/test/suite.cpp b/src/lib/test/suite.cpp index dd8e35bfe..6ad3ea887 100644 --- a/src/lib/test/suite.cpp +++ b/src/lib/test/suite.cpp @@ -165,6 +165,7 @@ namespace test { { try { + INFO (test, "++------------------- invoking TEST: %s", showType(theTest).c()); theTest.run (cmdline); return Suite::TEST_OK; } diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index eecc9d4fb..7ba45d628 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -149,7 +149,7 @@ namespace asset { asset::Media* pM (0); AssetManager& aMang = AssetManager::instance(); - TODO ("check and fix Category if necessary"); + //////////////////////////////////////////////////////////TICKET #841 check and fix Category if necessary if (isnil (file)) { @@ -168,7 +168,7 @@ namespace asset { MediaDesc& handle = maf.queryFile(key.name); Duration length = handle.length; - TODO ("detecting and wiring multichannel compound media!"); + //////////////////////////////////////////////////////////TICKET #841 detecting and wiring multichannel compound media pM = new Media (key,file,length); } ASSERT (pM); diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 6677b0cca..0de48828e 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -111,11 +111,11 @@ namespace asset { { AssetManager& _aMang (AssetManager::instance()); DB& registry (_aMang.registry); - TODO ("check validity of Ident Category"); + //////////////////////////////////////////////////////////TICKET #840 check validity of Ident Category ID asset_id (getID (idi)); DB::Lock guard(®istry); - TODO ("handle duplicate Registrations"); + //////////////////////////////////////////////////////////TICKET #840 handle duplicate Registrations P smart_ptr (obj, &destroy); registry.put (asset_id, smart_ptr); diff --git a/src/proc/mobject/session/sess-manager-impl.cpp b/src/proc/mobject/session/sess-manager-impl.cpp index d0e4c4cc4..972a711be 100644 --- a/src/proc/mobject/session/sess-manager-impl.cpp +++ b/src/proc/mobject/session/sess-manager-impl.cpp @@ -210,7 +210,7 @@ namespace session { SessManagerImpl::~SessManagerImpl () { - TODO ("verify sane lifecycle"); + //////////////////////////////////////// TICKET #845 verify sane session manager lifecycle here Session::initFlag = false; } From ebd6cfca82369fc3376275f0a72c0d728d0a38a8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 22 May 2011 05:33:08 +0200 Subject: [PATCH 008/296] Start design work for the Player Subsystem --- wiki/renderengine.html | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index b48372abb..4b7b80c02 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1632,6 +1632,26 @@ The main tool used to implement this separation is the [[Builder Pattern|http:// Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways. +
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
+
+!Starting point
+The idea is to start out with the design of the PlayerDummy and to //transform//&nbsp; it into the full player subsystem.
+* the ~DisplayService in that dummy player design moves down into Proc-Layer and becomes the OutputManager
+* likewise, the ~DisplayerSlot is transformed into the interface OutputSlot, with various implementations to be registered with the OutputManager
+* the core idea of having a play controler act as the frontend and handle to an PlayProcess is retained.
+
+!!Stages of transition
+Reworking the dummy player into the PlayerSubsystem is a larger undertaking, and best broken up into several //stages.//
+# just moving the existing entities into the final location (typically down in the layer hierarchy)
+# introduce a GeneratorMO and a GeneratorNode, while providing a shortcut to get the GeneratorNode without requiring the (not yet implemented) Builder.
+# rework the ~ProcessImpl and the ~TickService to become the CalculationStream and use the real Engine Interface, but still with an mock implementation hooked up behind
+# extend and elaborate the handling of output, build the foundation of the real OutputManager
+# switch to the real Scheduler and Engine implementation
+
+!!Idea
+Introduce a new kind of MObject, a GeneratorMO, to represent what the current dummy player does: generate some kind of test data. As a first step, we might retro-fit the existing dummy player to this structure, and then expand it to have a minimal render engine setup for tests and diagnostics. That is, there will be some kind of GeneratorNode to produce test data.
+
LayerSeparationInterface provided by the GUI.
 Access point especially for the playback. A render- or playback process uses the DisplayFacade to push media data up to the GUI for display within a viewer widget of full-screen display. This can be thought off as a callback mechanism. In order to use the DisplayFacade, client code needs a DisplayerSlot (handle), which needs to be set up by the UI first and will be provided when starting the render or playback process.
@@ -3177,7 +3197,7 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
 As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3193,7 +3213,7 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and &mdash; at the same time &mdash; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting connections at the "first of each kind".
+[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and &mdash; at the same time &mdash; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
 
 We should note that in both cases this mapping operation is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
@@ -4103,7 +4123,7 @@ The play process is an conceptual entity linking together several activities in
 * the RenderEngine has the ability to cary out individual frame calculations
 * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
-
+
Within Lumiera, &raquo;Player&laquo; denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
 The player subsystem does not perform or even manage any render operations, nor does it handle the outputs directly.
 Yet it adresses some central concerns:
@@ -4117,8 +4137,8 @@ Yet it adresses some central concerns:
 :the player translates continuous time values into discrete frame counts.
 :to perform this [[quantisation|TimeQuant]], the help of the session for building a TimeGrid for each output channel is required.
 
-!{{red{WIP 12/10}}} under construction
-The player subsystem is currently about to be designed and built up; some time ago, __Joel Holdsworth__ and __Ichthyo__ did a design study with a PlayerDummy, which is currently hooked up with the TransportControl in the Lumiera GUI.
+!{{red{WIP 5/2011}}} under construction
+The player subsystem is currently about to be designed and built up; some time ago, __Joel Holdsworth__ and __Ichthyo__ did a design study with a PlayerDummy, which is currently hooked up with the TransportControl in the Lumiera GUI. Starting from these experiences, and the general requirements of an NLE, the [[design of the Player subsystem|DesignPlayerSubsystem]] is being worked out.
 
From 899ffa60ca7afdd45ded9ee220c8b1c00f0f9c81 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 23 May 2011 04:43:56 +0200 Subject: [PATCH 009/296] WIP create (planned) new entities for the player subsystem --- src/backend/engine/scheduler-frontend.cpp | 33 ++ src/backend/engine/scheduler-frontend.hpp | 58 +++ src/proc/asset/viewer.cpp | 72 ++++ src/proc/asset/viewer.hpp | 115 ++++++ src/proc/engine/dispatcher.cpp | 42 ++ src/proc/engine/dispatcher.hpp | 64 +++ src/proc/engine/engine-service.cpp | 382 ++++++++++++++++++ src/proc/engine/engine-service.hpp | 161 ++++++++ .../worker}/dummy-image-generator.cpp | 0 .../worker}/dummy-image-generator.hpp | 0 .../{play => engine/worker}/tick-service.hpp | 0 src/proc/mobject/session/generator-mo.cpp | 86 ++++ src/proc/mobject/session/generator-mo.hpp | 110 +++++ src/proc/play/dummy-play-connection.cpp | 382 ++++++++++++++++++ src/proc/play/output-manager.cpp | 252 ++++++++++++ src/proc/play/output-manager.hpp | 190 +++++++++ src/proc/play/play-controller.hpp | 161 ++++++++ src/proc/play/play-process.cpp | 382 ++++++++++++++++++ src/proc/play/play-process.hpp | 161 ++++++++ src/proc/play/play-service.cpp | 382 ++++++++++++++++++ src/proc/play/play-service.hpp | 161 ++++++++ src/proc/play/sound/jack-output.cpp | 252 ++++++++++++ src/proc/play/sound/jack-output.hpp | 190 +++++++++ 23 files changed, 3636 insertions(+) create mode 100644 src/backend/engine/scheduler-frontend.cpp create mode 100644 src/backend/engine/scheduler-frontend.hpp create mode 100644 src/proc/asset/viewer.cpp create mode 100644 src/proc/asset/viewer.hpp create mode 100644 src/proc/engine/dispatcher.cpp create mode 100644 src/proc/engine/dispatcher.hpp create mode 100644 src/proc/engine/engine-service.cpp create mode 100644 src/proc/engine/engine-service.hpp rename src/proc/{play => engine/worker}/dummy-image-generator.cpp (100%) rename src/proc/{play => engine/worker}/dummy-image-generator.hpp (100%) rename src/proc/{play => engine/worker}/tick-service.hpp (100%) create mode 100644 src/proc/mobject/session/generator-mo.cpp create mode 100644 src/proc/mobject/session/generator-mo.hpp create mode 100644 src/proc/play/dummy-play-connection.cpp create mode 100644 src/proc/play/output-manager.cpp create mode 100644 src/proc/play/output-manager.hpp create mode 100644 src/proc/play/play-controller.hpp create mode 100644 src/proc/play/play-process.cpp create mode 100644 src/proc/play/play-process.hpp create mode 100644 src/proc/play/play-service.cpp create mode 100644 src/proc/play/play-service.hpp create mode 100644 src/proc/play/sound/jack-output.cpp create mode 100644 src/proc/play/sound/jack-output.hpp diff --git a/src/backend/engine/scheduler-frontend.cpp b/src/backend/engine/scheduler-frontend.cpp new file mode 100644 index 000000000..588d1276b --- /dev/null +++ b/src/backend/engine/scheduler-frontend.cpp @@ -0,0 +1,33 @@ +/* + RenderEngine - a complete network of processing nodes usable for rendering + + 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/engine/renderengine.hpp" + +namespace engine { + + + /** */ + + + +} // namespace engine diff --git a/src/backend/engine/scheduler-frontend.hpp b/src/backend/engine/scheduler-frontend.hpp new file mode 100644 index 000000000..c3030271f --- /dev/null +++ b/src/backend/engine/scheduler-frontend.hpp @@ -0,0 +1,58 @@ +/* + RENDERENGINE.hpp - a complete network of processing nodes usable for rendering + + 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 ENGINE_RENDERENGINE_H +#define ENGINE_RENDERENGINE_H + +#include + +#include "proc/engine/rendergraph.hpp" + + +using std::list; + + +namespace engine { + + + /** + * @todo this is planned to become the frontend + * to the render node network, which can be considered + * at the lower end of the middle layer; the actual + * render operations are mostly implemented by the backend + * ////////TODO WIP as of 12/2010 + */ + class RenderEngine : public RenderGraph + { + public: + ///// TODO: find out about the public operations + // note: the play controller lives in the proc-layer, + // but is a subsystem separate of the sesison. + + private: + list renderSegments; + + }; + +} // namespace engine +#endif diff --git a/src/proc/asset/viewer.cpp b/src/proc/asset/viewer.cpp new file mode 100644 index 000000000..bb2f9e36e --- /dev/null +++ b/src/proc/asset/viewer.cpp @@ -0,0 +1,72 @@ +/* + Timeline - independent top-level element of the Session + + Copyright (C) Lumiera.org + 2009, 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/asset/timeline.hpp" +//#include "proc/mobject/session/track.hpp" +//#include "proc/mobject/placement.hpp" +//#include "proc/mobject/session/mobjectfactory.hpp" +#include "proc/mobject/session/binding.hpp" +#include "proc/assetmanager.hpp" + + +namespace asset { + + using lib::AutoRegistered; + + + + /** @todo anything significant to do here??? */ + Timeline::Timeline (const Asset::Ident& idi, RBinding const& sequenceBinding) + : Struct (idi) + , boundSequence_(sequenceBinding) + { + REQUIRE (boundSequence_); + } + + + PTimeline + Timeline::create (Asset::Ident const& idi, RBinding const& sequenceBinding) + { + REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation"); + + PTimeline newElement (AssetManager::instance().wrap (*new Timeline(idi, sequenceBinding))); + getRegistry().append (newElement); + + ENSURE (newElement); + ENSURE (getRegistry().isRegistered (*newElement)); + return newElement; + } + + + void + Timeline::unlink () + { + AutoRegistered::detach(); + boundSequence_.purge(); + Struct::unlink(); + } + + + + +} // namespace asset diff --git a/src/proc/asset/viewer.hpp b/src/proc/asset/viewer.hpp new file mode 100644 index 000000000..7bf168e1e --- /dev/null +++ b/src/proc/asset/viewer.hpp @@ -0,0 +1,115 @@ +/* + TIMELINE.hpp - independent top-level element of the Session + + Copyright (C) Lumiera.org + 2009, 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 timeline.hpp + ** Top level structural element within the session. + ** Each Lumiera session may contain multiple top level timeline containers, + ** which at the same time act as structural asset and as part of the public + ** session API exposed to clients for discovering the session contents. + ** Actually, Timelines are facade objects, delegating the implementation to + ** the BindingMO, the Axis and the Sequences/Tracks. + ** + ** Contrary to usual habits in video/sound editing software, in Lumiera the + ** tracks are \em not part of the timeline, but rather attached directly to + ** the sequence container. To be usable, a timeline needs a binding to refer + ** to such a sequence, but this sequence may be bound into multiple timelines + ** or even virtual clips simultaneously. + ** + ** Like every structural asset, the creation of timelines happens automatically + ** on referral; Timelines can be queried from the StructFactory, providing additional + ** requested capabilities. Commonly clients will retrieve a given timeline by query + ** on the name-ID of the timeline: \c Struct::retrieve(Query("id(theName).")) + ** Additionally, the binding to a specific sequence may be established alongside: + ** \c "timeline(theTimelineName),bindSequence(theTimelineName,sequenceID)." + ** + ** @see Session + ** @see Sequence + ** @see StructFactory + ** + */ + + +#ifndef ASSET_TIMELINE_H +#define ASSET_TIMELINE_H + +#include "proc/asset/struct.hpp" +//#include "proc/mobject/mobject.hpp" +//#include "proc/mobject/placement.hpp" +#include "proc/mobject/mobject-ref.hpp" +//#include "proc/mobject/session/binding.hpp" ////TODO avoidable?? +#include "lib/p.hpp" +#include "lib/element-tracker.hpp" + + +//#include +//#include + +//using std::vector; +//using std::string; + +namespace mobject { +namespace session { + + class Binding; + typedef MORef RBinding; +}} + + +namespace asset { + + +// using lumiera::P; + class Timeline; + typedef lumiera::P PTimeline; + + + /** + * TODO type comment + */ + class Timeline + : public Struct + , public lib::AutoRegistered + { + typedef mobject::session::RBinding RBinding; + + RBinding boundSequence_; + + Timeline (Ident const&, RBinding const&); + + public: + /** create and register a new Timeline instance */ + static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding); + + protected: + virtual void unlink (); + + }; + + + + +///////////////////////////TODO currently just fleshing the API + + +} // namespace asset +#endif diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp new file mode 100644 index 000000000..fefaf7c63 --- /dev/null +++ b/src/proc/engine/dispatcher.cpp @@ -0,0 +1,42 @@ +/* + RenderGraph - render network corresponding to one segment of the timeline + + 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/engine/rendergraph.hpp" +#include "lib/frameid.hpp" +#include "proc/state.hpp" + +namespace lumiera { + + /** storage for the unique node-ID counter */ + ulong NodeID::currID (0); +} + + +namespace engine { + + /** */ + + + + +} // namespace engine diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp new file mode 100644 index 000000000..a77eac68b --- /dev/null +++ b/src/proc/engine/dispatcher.hpp @@ -0,0 +1,64 @@ +/* + RENDERGRAPH.hpp - render network corresponding to one segment of the timeline + + 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 ENGINE_RENDERGRAPH_H +#define ENGINE_RENDERGRAPH_H + +#include "proc/common.hpp" +#include "proc/state.hpp" +#include "lib/time/timevalue.hpp" + + + +namespace engine { + + using lib::time::TimeSpan; + using lib::time::FSecs; + using lib::time::Time; + + class ExitNode; + + /** + * @todo likely to be reworked into the engine backbone /////////////TODO WIP as of 12/2010 + */ + class RenderGraph + { + protected: + ExitNode * output; + + /** timerange covered by this RenderGraph */ + TimeSpan segment_; + + public: + RenderGraph() + : segment_(Time::ZERO, FSecs(5)) + { + UNIMPLEMENTED ("anything regarding the Fixture datastructure"); + } + + }; + + + +} // namespace engine +#endif diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp new file mode 100644 index 000000000..ea443f709 --- /dev/null +++ b/src/proc/engine/engine-service.cpp @@ -0,0 +1,382 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, 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/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "proc/play/tick-service.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include +#include +#include + + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using std::auto_ptr; + using boost::scoped_ptr; + using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + thePlayer_.reset(0); + // note: shutdown of the DummyPlayerService instance may block + // for a short period, until termination of all tick services + } + + bool + checkRunningState () throw() + { + return (thePlayer_); + } + }; + + lib::Singleton theDummyPlayerDescriptor; + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + 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_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef ProcessImpl* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (_instance->start(viewerHandle)); + } + ) + , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->doPlay(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + ProcessImpl::terminate (proc); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) + { + INFO (progress, "DummyPlayer Facade opened."); + } + + + + + /** @par implementation note + * A new process (implementation) is created, configured + * and started here. This may include spawning a thread or + * allocating a timer. The newly created process is self-contained + * and will be just handed out, without caring for its lifecycle. + * If client code accesses this function via the plain C interface, + * the client is responsible for terminating this process, whereas + * when using the C++ interface, you'll get a Handle object which + * manages the lifecycle automatically. + */ + ProcessImpl* + DummyPlayerService::start (LumieraDisplaySlot viewerHandle) + { + auto_ptr newProcess (new ProcessImpl (viewerHandle)); + + REQUIRE (!newProcess->isActive()); + newProcess->setRate(25); + + return newProcess.release(); + } + + + + + + + /* === Process Implementation === */ + + + ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) + : fps_(0) + , play_(false) + , display_(Display::facade().getHandle (viewerHandle)) + , imageGen_(0) + , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) + { } + + + ProcessImpl::~ProcessImpl() + { + INFO (proc_dbg, "Playback process halted..."); + } + + + void + ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle + { + if (process) + delete process; + } + + + + DummyPlayer::Process + ProcessImpl::createHandle() + { + DummyPlayer::Process handle; + handle.activate(this, &terminate); // note the deleter function... + return handle; + } + + + + void + ProcessImpl::setRate (uint fps) + { + REQUIRE (fps==0 || fps_==0 ); + REQUIRE (fps==0 || !play_ ); + REQUIRE (tick_); + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + + // callbacks with given frequency, starting now + tick_->activate(fps); + } + + + + void + ProcessImpl::doPlay(bool yes) + { + REQUIRE (isActive()); + tick_->activate (yes? fps_:0); + play_ = yes; + } + + + + void + ProcessImpl::doFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + display_(imageGen_->next()); + else + display_(imageGen_->current()); + } + + + + } // namespace play + +} // namespace proc + + + + + +namespace lumiera { /* === Forwarding function(s) on the Process handle === */ + + void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } + + + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return proc::play::theDummyPlayerDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + + +} // namespace lumiera diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp new file mode 100644 index 000000000..75e0f3d78 --- /dev/null +++ b/src/proc/engine/engine-service.hpp @@ -0,0 +1,161 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, 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 dummy-player-service.hpp + ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. + ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The + ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding + ** the necessary handles and allocations and providing an uniform API to the client side. + ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and + ** it uses an output handle (functor) to push the generated frames up. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + using lumiera::Display; + using lumiera::DummyPlayer; + + + class DummyImageGenerator; + class TickService; + + + /******************************************************************** + * Actual implementation of a single (dummy) playback process. + * The DummyPlayerService (see below) maintains a collection of such + * actively running playback processes, while the client code gets + * DummyPlayer::Process handles to track any ongoing use. Users of + * the plain C interface get a direct bare pointer to the respective + * ProcessImpl instance and have to manage the lifecycle manually. + */ + class ProcessImpl + : public lumiera_playprocess, + boost::noncopyable + { + uint fps_; + bool play_; + + Display::Sink display_; + boost::scoped_ptr imageGen_; + boost::scoped_ptr tick_; + + + public: + ProcessImpl(LumieraDisplaySlot) ; + ~ProcessImpl() ; + + + /* Implementation-level API */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + + void doPlay(bool yes); + + + /* Lifecycle */ + + DummyPlayer::Process createHandle(); + static void terminate(ProcessImpl* process); + + private: + void doFrame (); ///< periodically invoked while playing + }; + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class DummyPlayerService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/dummy-image-generator.cpp b/src/proc/engine/worker/dummy-image-generator.cpp similarity index 100% rename from src/proc/play/dummy-image-generator.cpp rename to src/proc/engine/worker/dummy-image-generator.cpp diff --git a/src/proc/play/dummy-image-generator.hpp b/src/proc/engine/worker/dummy-image-generator.hpp similarity index 100% rename from src/proc/play/dummy-image-generator.hpp rename to src/proc/engine/worker/dummy-image-generator.hpp diff --git a/src/proc/play/tick-service.hpp b/src/proc/engine/worker/tick-service.hpp similarity index 100% rename from src/proc/play/tick-service.hpp rename to src/proc/engine/worker/tick-service.hpp diff --git a/src/proc/mobject/session/generator-mo.cpp b/src/proc/mobject/session/generator-mo.cpp new file mode 100644 index 000000000..426cd8e57 --- /dev/null +++ b/src/proc/mobject/session/generator-mo.cpp @@ -0,0 +1,86 @@ +/* + Clip - a Media Clip + + 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/mobject/session/clip.hpp" +#include "proc/assetmanager.hpp" +#include "proc/asset/media.hpp" +#include "proc/asset/clip.hpp" +#include "lib/time/mutation.hpp" +#include "lib/util.hpp" + +using lib::time::Mutation; +using util::isnil; + +namespace mobject { +namespace session { + + /** new clip-MO linked with the given asset::Clip. + * Initially, this clip will cover the whole source media length. + */ + Clip::Clip (const asset::Clip& clipDef, const Media& mediaDef) + : mediaDef_(mediaDef) + , clipDef_(clipDef) + { + setupLength(); + throwIfInvalid(); + } + + + + /** implementing the common MObject self test. + * Length definition is consitent, underlying + * media def is accessible etc. */ + bool + Clip::isValid () const + { + TODO ("check consistency of clip length def, implies accessing the underlying media def"); + return !isnil(length_); + } + + + void + Clip::setupLength() + { + TODO ("really calculate the length of a clip and set length field"); + this->length_.accept (Mutation::changeDuration(mediaDef_.getLength())); + } + + + PMedia + Clip::getMedia () const + { + return asset::AssetManager::wrap (mediaDef_); + } + + + PClipAsset + Clip::findClipAsset () const + { + return asset::AssetManager::wrap (clipDef_); + } + + + + +}} // namespace mobject::session + diff --git a/src/proc/mobject/session/generator-mo.hpp b/src/proc/mobject/session/generator-mo.hpp new file mode 100644 index 000000000..52e134926 --- /dev/null +++ b/src/proc/mobject/session/generator-mo.hpp @@ -0,0 +1,110 @@ +/* + CLIP.hpp - a Media Clip + + 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_SESSION_CLIP_H +#define MOBJECT_SESSION_CLIP_H + +#include "proc/mobject/session/abstractmo.hpp" +#include "lib/time/timevalue.hpp" + + +namespace asset { + class Media; + class Clip; +} + +namespace mobject { +namespace session { + + using asset::Media; + using lib::time::TimeVar; + + typedef P PMedia; + typedef P PClipAsset; + + + /** + * A user visible/editable Clip is a reference to a contiguous + * sequence of media data loaded as Asset into the current Session. + * As such, it is a virtual (non destructive) cut or edit of the + * source material and can be placed into the Session to be rendered + * into the output. The actual media type of a clip will be derived + * at runtime by resolving this reference to the underlying Asset. + * + * @todo define how to denote Time positions /lengths. This is tricky, + * because it depends on the actual media type, and we want to encapsulate + * all these details as much as possible. + */ + class Clip + : public AbstractMO + { + string + initShortID() const + { + return buildShortID("Clip"); + } + + void setupLength(); + + + + protected: + /** start position in source */ + TimeVar start_; + + /** @todo using a mere ref here is against the scheme and only + done as temporal solution, until we work out how to handle + multichannel clips. It should be a smart pointer of some kind + and the unlink() function of the asset should take it into + account when breaking circular references. + */ + + const Media & mediaDef_; + const asset::Clip & clipDef_; + + Clip (const asset::Clip&, const Media&); + friend class MObjectFactory; + + + public: + bool isValid() const; + + /** access the underlying media asset */ + PMedia getMedia () const; + + /** locate the corresponding asset + * representing this clip or the whole + * compound in case of a multichannel clip + */ + PClipAsset findClipAsset () const; + + DEFINE_PROCESSABLE_BY (builder::BuilderTool); + + }; + + typedef Placement PClipMO; + + + +}} // namespace mobject::session +#endif diff --git a/src/proc/play/dummy-play-connection.cpp b/src/proc/play/dummy-play-connection.cpp new file mode 100644 index 000000000..ea443f709 --- /dev/null +++ b/src/proc/play/dummy-play-connection.cpp @@ -0,0 +1,382 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, 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/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "proc/play/tick-service.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include +#include +#include + + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using std::auto_ptr; + using boost::scoped_ptr; + using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + thePlayer_.reset(0); + // note: shutdown of the DummyPlayerService instance may block + // for a short period, until termination of all tick services + } + + bool + checkRunningState () throw() + { + return (thePlayer_); + } + }; + + lib::Singleton theDummyPlayerDescriptor; + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + 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_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef ProcessImpl* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (_instance->start(viewerHandle)); + } + ) + , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->doPlay(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + ProcessImpl::terminate (proc); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) + { + INFO (progress, "DummyPlayer Facade opened."); + } + + + + + /** @par implementation note + * A new process (implementation) is created, configured + * and started here. This may include spawning a thread or + * allocating a timer. The newly created process is self-contained + * and will be just handed out, without caring for its lifecycle. + * If client code accesses this function via the plain C interface, + * the client is responsible for terminating this process, whereas + * when using the C++ interface, you'll get a Handle object which + * manages the lifecycle automatically. + */ + ProcessImpl* + DummyPlayerService::start (LumieraDisplaySlot viewerHandle) + { + auto_ptr newProcess (new ProcessImpl (viewerHandle)); + + REQUIRE (!newProcess->isActive()); + newProcess->setRate(25); + + return newProcess.release(); + } + + + + + + + /* === Process Implementation === */ + + + ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) + : fps_(0) + , play_(false) + , display_(Display::facade().getHandle (viewerHandle)) + , imageGen_(0) + , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) + { } + + + ProcessImpl::~ProcessImpl() + { + INFO (proc_dbg, "Playback process halted..."); + } + + + void + ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle + { + if (process) + delete process; + } + + + + DummyPlayer::Process + ProcessImpl::createHandle() + { + DummyPlayer::Process handle; + handle.activate(this, &terminate); // note the deleter function... + return handle; + } + + + + void + ProcessImpl::setRate (uint fps) + { + REQUIRE (fps==0 || fps_==0 ); + REQUIRE (fps==0 || !play_ ); + REQUIRE (tick_); + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + + // callbacks with given frequency, starting now + tick_->activate(fps); + } + + + + void + ProcessImpl::doPlay(bool yes) + { + REQUIRE (isActive()); + tick_->activate (yes? fps_:0); + play_ = yes; + } + + + + void + ProcessImpl::doFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + display_(imageGen_->next()); + else + display_(imageGen_->current()); + } + + + + } // namespace play + +} // namespace proc + + + + + +namespace lumiera { /* === Forwarding function(s) on the Process handle === */ + + void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } + + + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return proc::play::theDummyPlayerDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + + +} // namespace lumiera diff --git a/src/proc/play/output-manager.cpp b/src/proc/play/output-manager.cpp new file mode 100644 index 000000000..96600bf3b --- /dev/null +++ b/src/proc/play/output-manager.cpp @@ -0,0 +1,252 @@ +/* + DisplayService - service providing access to a display for outputting frames + + Copyright (C) Lumiera.org + 2009, 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 "gui/display-service.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + + +namespace gui { + + + + namespace { // hidden local details of the service implementation.... + + + + /* ================== define an lumieraorg_Display instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DisplayFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\323\343\324\023\064\216\120\201\073\056\366\020\110\263\060\023", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Display"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\305\026\070\133\033\357\014\202\203\270\174\072\341\256\226\235", + const char*, (LumieraInterface ifa), + { (void)ifa; return "UI Interface: service for outputting frames to a viewer or display"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\170\104\246\175\123\144\332\312\315\263\071\170\164\213\024\275", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\265\343\045\346\110\241\276\111\217\120\155\246\230\341\344\124", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\302\027\122\045\301\166\046\236\257\253\144\035\105\166\070\103", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\074\013\020\161\075\135\302\265\260\000\301\147\116\355\035\261", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\037\232\153\100\114\103\074\342\164\132\370\210\372\164\115\275", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\026\243\334\056\125\245\315\311\155\375\262\344\007\076\341\254", + const char*, (LumieraInterface ifa), + { + (void)ifa; + 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_INTERFACE_INLINE (state, "\243\302\332\160\060\272\155\334\212\256\303\141\160\063\164\154", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\363\125\123\060\231\147\053\017\131\341\105\157\231\273\334\136", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DisplayService implementation... + + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_Display, 0 + ,lumieraorg_DisplayService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DisplayFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (allocate, "\177\221\146\253\255\161\160\137\015\005\263\362\307\022\243\365", + void, (LumieraDisplaySlot slotHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (slotHandle); + try + { + _instance->allocate (slotHandle,true); + } + catch (lumiera::Error&){ /* error state remains set */ } + } + ) + , LUMIERA_INTERFACE_INLINE (release, "\166\374\106\313\011\142\115\161\111\110\376\016\346\115\240\364", + void, (LumieraDisplaySlot slotHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (slotHandle); + _instance->allocate (slotHandle,false); + } + ) + , LUMIERA_INTERFACE_INLINE (put, "\340\062\234\227\152\131\370\272\146\207\224\015\361\070\252\135", + void, (LumieraDisplaySlot slotHandle, LumieraDisplayFrame frame), + { + //skipping full checks for performance reasons + REQUIRE (_instance && !lumiera_error_peek()); + + REQUIRE (slotHandle); + DisplayerSlot& slot = _instance->resolve (slotHandle); + slot.put (frame); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DisplayService::DisplayService() + : error_("") + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_Display, 0, lumieraorg_DisplayService)) + { + INFO (progress, "Display Facade opened."); + } + + + + LumieraDisplaySlot + DisplayService::setUp (FrameDestination const& outputDestination) + { + DisplayerTab& slots (_instance->slots_); + return &slots.manage (new DisplayerSlot (outputDestination)); + } + + + + void + DisplayService::allocate (LumieraDisplaySlot handle, bool doAllocate) + { + REQUIRE (handle); + if (doAllocate) + { + if (handle->put_) + throw lumiera::error::Logic("slot already allocated for output"); + else + // Mark the handle as "allocated" and ready for output: + // Place the function pointer from the C interface into the handle struct. + // calling it will invoke the implementing instance's "put" function + // (see the LUMIERA_INTERFACE_INLINE above in this file!) + handle->put_ = serviceInstance_.get().put; + } + else + handle->put_ = 0; + } + + + + DisplayerSlot& + DisplayService::resolve (LumieraDisplaySlot handle) + { + REQUIRE (handle); + REQUIRE (handle->put_, "accessing a DisplayerSlot, which hasn't been locked for output"); + + return *static_cast (handle); + } + + + + + + /* === DisplayerSlot Implementation === */ + + + DisplayerSlot::DisplayerSlot (FrameDestination const& outputDestination) + : currBuffer_(0) + { + put_ = 0; // mark as not allocated + hasFrame_.connect (outputDestination); + dispatcher_.connect (sigc::mem_fun (this, &DisplayerSlot::displayCurrentFrame)); + } + + + DisplayerSlot::~DisplayerSlot() + { + TRACE (gui_dbg, "Displayer Slot closing..."); + } + + + void + DisplayerSlot::displayCurrentFrame() + { + hasFrame_.emit (currBuffer_); + } + + +} // namespace proc diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp new file mode 100644 index 000000000..1dec2c192 --- /dev/null +++ b/src/proc/play/output-manager.hpp @@ -0,0 +1,190 @@ +/* + DISPLAY-SERVICE.hpp - service providing access to a display for outputting frames + + Copyright (C) Lumiera.org + 2009, 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 display-service.hpp + ** A public service provided by the GUI, implementing the lumiera::Display facade interface. + ** It serves two purposes: + ** - It maintains a collection of DisplayerSlot objects, which are the actual connection points + ** and allow to receive frames and dispatch them to the GTK main event loop thread. + ** Conceptually, creating such a slot means providing a possible display for output. + ** - It provides the actual implementation of the Display facade interface, i.e. the function + ** which is to invoked periodically by the playback processes to dispose a new frame into + ** the display. + ** + ** This service is the implementation of a layer separation facade interface. This header defines + ** the interface used to \em provide this service, not to access it. Clients get a specific + ** LumieraDisplaySlot passed as parameter when initiating a playback process from the GUI. Using + ** this LumieraDisplaySlot handle, clients should then use lumiera::DummyPlayer#facade to access + ** an implementation instance of this service in order to push actual frames up. + ** + ** @see lumiera::Display + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef GUI_DISPLAY_SERVICE_H +#define GUI_DISPLAY_SERVICE_H + + +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" +#include "lib/scoped-ptrvect.hpp" + +#include +#include +#include +#include +#include + + +namespace gui { + + using std::string; + using std::vector; + using lumiera::Display; + using Glib::Dispatcher; + + + typedef sigc::slot FrameDestination; + typedef sigc::signal FrameSignal; + + + + /******************************************************************** + * Actual implementation of a single displayer slot. Internally, + * it is connected via the Glib::Dispatcher for outputting frames + * to a viewer widget, which executes within the GTK event thread. + * @note must be created from the GTK event thread. + */ + class DisplayerSlot + : public lumiera_displaySlot, + boost::noncopyable + { + Dispatcher dispatcher_; + FrameSignal hasFrame_; + + LumieraDisplayFrame currBuffer_; + + + public: + DisplayerSlot (FrameDestination const&) ; + ~DisplayerSlot () ; + + /* Implementation-level API to be used by DisplayService */ + + /** receive a frame to be displayed */ + inline void put (LumieraDisplayFrame); + + + private: + /** internal: activated via Dispatcher + * and running in GTK main thread */ + void displayCurrentFrame(); + + }; + + typedef lib::ScopedPtrVect DisplayerTab; + + + + /****************************************************** + * Actual implementation of the DisplayService. + * Creating an instance of this class automatically + * registers the interface lumieraorg_Display with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + * \par + * In addition to the Display interface, this class + * implements an additional service for the GUI, + * allowing actually to set up display slots, which + * then can be handed out to client code in the + * course of the play process for outputting frames. + */ + class DisplayService + : boost::noncopyable + { + + string error_; + DisplayerTab slots_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_Display, 0) + , lumiera::Display + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + + public: + DisplayService(); + ~DisplayService() { + INFO (proc_dbg, "Display service dying..."); + + } + + + /** open a new display, sending frames to the given output destination + * @return handle for this slot, can be used to start a play process. + * NULL handle in case of any error. */ + static LumieraDisplaySlot setUp (FrameDestination const&); + + + /** prepare and the given slot for output + * @param doAllocate allocate when true, else release it + * @throw lumiera::error::Logic when already in use */ + void allocate (LumieraDisplaySlot, bool doAllocate); + + + /** resolve the given display slot handle to yield a ref + * to an actual implementation object. In order to be resolvable, + * the DisplayerSlot needs to be locked (=allocated) for output use. */ + DisplayerSlot& resolve (LumieraDisplaySlot); + + }; + + + + + void + DisplayerSlot::put(LumieraDisplayFrame newFrame) + { + if (newFrame != currBuffer_) + { + currBuffer_ = newFrame; + dispatcher_.emit(); + } + else + { + TRACE (render, "frame dropped?"); + } + } + + + +} // namespace gui +#endif diff --git a/src/proc/play/play-controller.hpp b/src/proc/play/play-controller.hpp new file mode 100644 index 000000000..75e0f3d78 --- /dev/null +++ b/src/proc/play/play-controller.hpp @@ -0,0 +1,161 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, 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 dummy-player-service.hpp + ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. + ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The + ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding + ** the necessary handles and allocations and providing an uniform API to the client side. + ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and + ** it uses an output handle (functor) to push the generated frames up. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + using lumiera::Display; + using lumiera::DummyPlayer; + + + class DummyImageGenerator; + class TickService; + + + /******************************************************************** + * Actual implementation of a single (dummy) playback process. + * The DummyPlayerService (see below) maintains a collection of such + * actively running playback processes, while the client code gets + * DummyPlayer::Process handles to track any ongoing use. Users of + * the plain C interface get a direct bare pointer to the respective + * ProcessImpl instance and have to manage the lifecycle manually. + */ + class ProcessImpl + : public lumiera_playprocess, + boost::noncopyable + { + uint fps_; + bool play_; + + Display::Sink display_; + boost::scoped_ptr imageGen_; + boost::scoped_ptr tick_; + + + public: + ProcessImpl(LumieraDisplaySlot) ; + ~ProcessImpl() ; + + + /* Implementation-level API */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + + void doPlay(bool yes); + + + /* Lifecycle */ + + DummyPlayer::Process createHandle(); + static void terminate(ProcessImpl* process); + + private: + void doFrame (); ///< periodically invoked while playing + }; + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class DummyPlayerService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp new file mode 100644 index 000000000..ea443f709 --- /dev/null +++ b/src/proc/play/play-process.cpp @@ -0,0 +1,382 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, 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/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "proc/play/tick-service.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include +#include +#include + + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using std::auto_ptr; + using boost::scoped_ptr; + using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + thePlayer_.reset(0); + // note: shutdown of the DummyPlayerService instance may block + // for a short period, until termination of all tick services + } + + bool + checkRunningState () throw() + { + return (thePlayer_); + } + }; + + lib::Singleton theDummyPlayerDescriptor; + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + 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_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef ProcessImpl* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (_instance->start(viewerHandle)); + } + ) + , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->doPlay(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + ProcessImpl::terminate (proc); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) + { + INFO (progress, "DummyPlayer Facade opened."); + } + + + + + /** @par implementation note + * A new process (implementation) is created, configured + * and started here. This may include spawning a thread or + * allocating a timer. The newly created process is self-contained + * and will be just handed out, without caring for its lifecycle. + * If client code accesses this function via the plain C interface, + * the client is responsible for terminating this process, whereas + * when using the C++ interface, you'll get a Handle object which + * manages the lifecycle automatically. + */ + ProcessImpl* + DummyPlayerService::start (LumieraDisplaySlot viewerHandle) + { + auto_ptr newProcess (new ProcessImpl (viewerHandle)); + + REQUIRE (!newProcess->isActive()); + newProcess->setRate(25); + + return newProcess.release(); + } + + + + + + + /* === Process Implementation === */ + + + ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) + : fps_(0) + , play_(false) + , display_(Display::facade().getHandle (viewerHandle)) + , imageGen_(0) + , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) + { } + + + ProcessImpl::~ProcessImpl() + { + INFO (proc_dbg, "Playback process halted..."); + } + + + void + ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle + { + if (process) + delete process; + } + + + + DummyPlayer::Process + ProcessImpl::createHandle() + { + DummyPlayer::Process handle; + handle.activate(this, &terminate); // note the deleter function... + return handle; + } + + + + void + ProcessImpl::setRate (uint fps) + { + REQUIRE (fps==0 || fps_==0 ); + REQUIRE (fps==0 || !play_ ); + REQUIRE (tick_); + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + + // callbacks with given frequency, starting now + tick_->activate(fps); + } + + + + void + ProcessImpl::doPlay(bool yes) + { + REQUIRE (isActive()); + tick_->activate (yes? fps_:0); + play_ = yes; + } + + + + void + ProcessImpl::doFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + display_(imageGen_->next()); + else + display_(imageGen_->current()); + } + + + + } // namespace play + +} // namespace proc + + + + + +namespace lumiera { /* === Forwarding function(s) on the Process handle === */ + + void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } + + + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return proc::play::theDummyPlayerDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + + +} // namespace lumiera diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp new file mode 100644 index 000000000..75e0f3d78 --- /dev/null +++ b/src/proc/play/play-process.hpp @@ -0,0 +1,161 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, 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 dummy-player-service.hpp + ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. + ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The + ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding + ** the necessary handles and allocations and providing an uniform API to the client side. + ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and + ** it uses an output handle (functor) to push the generated frames up. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + using lumiera::Display; + using lumiera::DummyPlayer; + + + class DummyImageGenerator; + class TickService; + + + /******************************************************************** + * Actual implementation of a single (dummy) playback process. + * The DummyPlayerService (see below) maintains a collection of such + * actively running playback processes, while the client code gets + * DummyPlayer::Process handles to track any ongoing use. Users of + * the plain C interface get a direct bare pointer to the respective + * ProcessImpl instance and have to manage the lifecycle manually. + */ + class ProcessImpl + : public lumiera_playprocess, + boost::noncopyable + { + uint fps_; + bool play_; + + Display::Sink display_; + boost::scoped_ptr imageGen_; + boost::scoped_ptr tick_; + + + public: + ProcessImpl(LumieraDisplaySlot) ; + ~ProcessImpl() ; + + + /* Implementation-level API */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + + void doPlay(bool yes); + + + /* Lifecycle */ + + DummyPlayer::Process createHandle(); + static void terminate(ProcessImpl* process); + + private: + void doFrame (); ///< periodically invoked while playing + }; + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class DummyPlayerService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp new file mode 100644 index 000000000..ea443f709 --- /dev/null +++ b/src/proc/play/play-service.cpp @@ -0,0 +1,382 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, 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/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "proc/play/tick-service.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include +#include +#include + + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using std::auto_ptr; + using boost::scoped_ptr; + using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + thePlayer_.reset(0); + // note: shutdown of the DummyPlayerService instance may block + // for a short period, until termination of all tick services + } + + bool + checkRunningState () throw() + { + return (thePlayer_); + } + }; + + lib::Singleton theDummyPlayerDescriptor; + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + 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_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef ProcessImpl* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (_instance->start(viewerHandle)); + } + ) + , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->doPlay(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + ProcessImpl::terminate (proc); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) + { + INFO (progress, "DummyPlayer Facade opened."); + } + + + + + /** @par implementation note + * A new process (implementation) is created, configured + * and started here. This may include spawning a thread or + * allocating a timer. The newly created process is self-contained + * and will be just handed out, without caring for its lifecycle. + * If client code accesses this function via the plain C interface, + * the client is responsible for terminating this process, whereas + * when using the C++ interface, you'll get a Handle object which + * manages the lifecycle automatically. + */ + ProcessImpl* + DummyPlayerService::start (LumieraDisplaySlot viewerHandle) + { + auto_ptr newProcess (new ProcessImpl (viewerHandle)); + + REQUIRE (!newProcess->isActive()); + newProcess->setRate(25); + + return newProcess.release(); + } + + + + + + + /* === Process Implementation === */ + + + ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) + : fps_(0) + , play_(false) + , display_(Display::facade().getHandle (viewerHandle)) + , imageGen_(0) + , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) + { } + + + ProcessImpl::~ProcessImpl() + { + INFO (proc_dbg, "Playback process halted..."); + } + + + void + ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle + { + if (process) + delete process; + } + + + + DummyPlayer::Process + ProcessImpl::createHandle() + { + DummyPlayer::Process handle; + handle.activate(this, &terminate); // note the deleter function... + return handle; + } + + + + void + ProcessImpl::setRate (uint fps) + { + REQUIRE (fps==0 || fps_==0 ); + REQUIRE (fps==0 || !play_ ); + REQUIRE (tick_); + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + + // callbacks with given frequency, starting now + tick_->activate(fps); + } + + + + void + ProcessImpl::doPlay(bool yes) + { + REQUIRE (isActive()); + tick_->activate (yes? fps_:0); + play_ = yes; + } + + + + void + ProcessImpl::doFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + display_(imageGen_->next()); + else + display_(imageGen_->current()); + } + + + + } // namespace play + +} // namespace proc + + + + + +namespace lumiera { /* === Forwarding function(s) on the Process handle === */ + + void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } + + + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return proc::play::theDummyPlayerDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + + +} // namespace lumiera diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp new file mode 100644 index 000000000..75e0f3d78 --- /dev/null +++ b/src/proc/play/play-service.hpp @@ -0,0 +1,161 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, 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 dummy-player-service.hpp + ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. + ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The + ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding + ** the necessary handles and allocations and providing an uniform API to the client side. + ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and + ** it uses an output handle (functor) to push the generated frames up. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + using lumiera::Display; + using lumiera::DummyPlayer; + + + class DummyImageGenerator; + class TickService; + + + /******************************************************************** + * Actual implementation of a single (dummy) playback process. + * The DummyPlayerService (see below) maintains a collection of such + * actively running playback processes, while the client code gets + * DummyPlayer::Process handles to track any ongoing use. Users of + * the plain C interface get a direct bare pointer to the respective + * ProcessImpl instance and have to manage the lifecycle manually. + */ + class ProcessImpl + : public lumiera_playprocess, + boost::noncopyable + { + uint fps_; + bool play_; + + Display::Sink display_; + boost::scoped_ptr imageGen_; + boost::scoped_ptr tick_; + + + public: + ProcessImpl(LumieraDisplaySlot) ; + ~ProcessImpl() ; + + + /* Implementation-level API */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + + void doPlay(bool yes); + + + /* Lifecycle */ + + DummyPlayer::Process createHandle(); + static void terminate(ProcessImpl* process); + + private: + void doFrame (); ///< periodically invoked while playing + }; + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class DummyPlayerService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/sound/jack-output.cpp b/src/proc/play/sound/jack-output.cpp new file mode 100644 index 000000000..96600bf3b --- /dev/null +++ b/src/proc/play/sound/jack-output.cpp @@ -0,0 +1,252 @@ +/* + DisplayService - service providing access to a display for outputting frames + + Copyright (C) Lumiera.org + 2009, 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 "gui/display-service.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + + +namespace gui { + + + + namespace { // hidden local details of the service implementation.... + + + + /* ================== define an lumieraorg_Display instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DisplayFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\323\343\324\023\064\216\120\201\073\056\366\020\110\263\060\023", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Display"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\305\026\070\133\033\357\014\202\203\270\174\072\341\256\226\235", + const char*, (LumieraInterface ifa), + { (void)ifa; return "UI Interface: service for outputting frames to a viewer or display"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\170\104\246\175\123\144\332\312\315\263\071\170\164\213\024\275", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\265\343\045\346\110\241\276\111\217\120\155\246\230\341\344\124", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\302\027\122\045\301\166\046\236\257\253\144\035\105\166\070\103", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\074\013\020\161\075\135\302\265\260\000\301\147\116\355\035\261", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\037\232\153\100\114\103\074\342\164\132\370\210\372\164\115\275", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\026\243\334\056\125\245\315\311\155\375\262\344\007\076\341\254", + const char*, (LumieraInterface ifa), + { + (void)ifa; + 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_INTERFACE_INLINE (state, "\243\302\332\160\060\272\155\334\212\256\303\141\160\063\164\154", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\363\125\123\060\231\147\053\017\131\341\105\157\231\273\334\136", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DisplayService implementation... + + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_Display, 0 + ,lumieraorg_DisplayService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DisplayFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (allocate, "\177\221\146\253\255\161\160\137\015\005\263\362\307\022\243\365", + void, (LumieraDisplaySlot slotHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (slotHandle); + try + { + _instance->allocate (slotHandle,true); + } + catch (lumiera::Error&){ /* error state remains set */ } + } + ) + , LUMIERA_INTERFACE_INLINE (release, "\166\374\106\313\011\142\115\161\111\110\376\016\346\115\240\364", + void, (LumieraDisplaySlot slotHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (slotHandle); + _instance->allocate (slotHandle,false); + } + ) + , LUMIERA_INTERFACE_INLINE (put, "\340\062\234\227\152\131\370\272\146\207\224\015\361\070\252\135", + void, (LumieraDisplaySlot slotHandle, LumieraDisplayFrame frame), + { + //skipping full checks for performance reasons + REQUIRE (_instance && !lumiera_error_peek()); + + REQUIRE (slotHandle); + DisplayerSlot& slot = _instance->resolve (slotHandle); + slot.put (frame); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DisplayService::DisplayService() + : error_("") + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_Display, 0, lumieraorg_DisplayService)) + { + INFO (progress, "Display Facade opened."); + } + + + + LumieraDisplaySlot + DisplayService::setUp (FrameDestination const& outputDestination) + { + DisplayerTab& slots (_instance->slots_); + return &slots.manage (new DisplayerSlot (outputDestination)); + } + + + + void + DisplayService::allocate (LumieraDisplaySlot handle, bool doAllocate) + { + REQUIRE (handle); + if (doAllocate) + { + if (handle->put_) + throw lumiera::error::Logic("slot already allocated for output"); + else + // Mark the handle as "allocated" and ready for output: + // Place the function pointer from the C interface into the handle struct. + // calling it will invoke the implementing instance's "put" function + // (see the LUMIERA_INTERFACE_INLINE above in this file!) + handle->put_ = serviceInstance_.get().put; + } + else + handle->put_ = 0; + } + + + + DisplayerSlot& + DisplayService::resolve (LumieraDisplaySlot handle) + { + REQUIRE (handle); + REQUIRE (handle->put_, "accessing a DisplayerSlot, which hasn't been locked for output"); + + return *static_cast (handle); + } + + + + + + /* === DisplayerSlot Implementation === */ + + + DisplayerSlot::DisplayerSlot (FrameDestination const& outputDestination) + : currBuffer_(0) + { + put_ = 0; // mark as not allocated + hasFrame_.connect (outputDestination); + dispatcher_.connect (sigc::mem_fun (this, &DisplayerSlot::displayCurrentFrame)); + } + + + DisplayerSlot::~DisplayerSlot() + { + TRACE (gui_dbg, "Displayer Slot closing..."); + } + + + void + DisplayerSlot::displayCurrentFrame() + { + hasFrame_.emit (currBuffer_); + } + + +} // namespace proc diff --git a/src/proc/play/sound/jack-output.hpp b/src/proc/play/sound/jack-output.hpp new file mode 100644 index 000000000..1dec2c192 --- /dev/null +++ b/src/proc/play/sound/jack-output.hpp @@ -0,0 +1,190 @@ +/* + DISPLAY-SERVICE.hpp - service providing access to a display for outputting frames + + Copyright (C) Lumiera.org + 2009, 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 display-service.hpp + ** A public service provided by the GUI, implementing the lumiera::Display facade interface. + ** It serves two purposes: + ** - It maintains a collection of DisplayerSlot objects, which are the actual connection points + ** and allow to receive frames and dispatch them to the GTK main event loop thread. + ** Conceptually, creating such a slot means providing a possible display for output. + ** - It provides the actual implementation of the Display facade interface, i.e. the function + ** which is to invoked periodically by the playback processes to dispose a new frame into + ** the display. + ** + ** This service is the implementation of a layer separation facade interface. This header defines + ** the interface used to \em provide this service, not to access it. Clients get a specific + ** LumieraDisplaySlot passed as parameter when initiating a playback process from the GUI. Using + ** this LumieraDisplaySlot handle, clients should then use lumiera::DummyPlayer#facade to access + ** an implementation instance of this service in order to push actual frames up. + ** + ** @see lumiera::Display + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef GUI_DISPLAY_SERVICE_H +#define GUI_DISPLAY_SERVICE_H + + +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" +#include "lib/scoped-ptrvect.hpp" + +#include +#include +#include +#include +#include + + +namespace gui { + + using std::string; + using std::vector; + using lumiera::Display; + using Glib::Dispatcher; + + + typedef sigc::slot FrameDestination; + typedef sigc::signal FrameSignal; + + + + /******************************************************************** + * Actual implementation of a single displayer slot. Internally, + * it is connected via the Glib::Dispatcher for outputting frames + * to a viewer widget, which executes within the GTK event thread. + * @note must be created from the GTK event thread. + */ + class DisplayerSlot + : public lumiera_displaySlot, + boost::noncopyable + { + Dispatcher dispatcher_; + FrameSignal hasFrame_; + + LumieraDisplayFrame currBuffer_; + + + public: + DisplayerSlot (FrameDestination const&) ; + ~DisplayerSlot () ; + + /* Implementation-level API to be used by DisplayService */ + + /** receive a frame to be displayed */ + inline void put (LumieraDisplayFrame); + + + private: + /** internal: activated via Dispatcher + * and running in GTK main thread */ + void displayCurrentFrame(); + + }; + + typedef lib::ScopedPtrVect DisplayerTab; + + + + /****************************************************** + * Actual implementation of the DisplayService. + * Creating an instance of this class automatically + * registers the interface lumieraorg_Display with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + * \par + * In addition to the Display interface, this class + * implements an additional service for the GUI, + * allowing actually to set up display slots, which + * then can be handed out to client code in the + * course of the play process for outputting frames. + */ + class DisplayService + : boost::noncopyable + { + + string error_; + DisplayerTab slots_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_Display, 0) + , lumiera::Display + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + + public: + DisplayService(); + ~DisplayService() { + INFO (proc_dbg, "Display service dying..."); + + } + + + /** open a new display, sending frames to the given output destination + * @return handle for this slot, can be used to start a play process. + * NULL handle in case of any error. */ + static LumieraDisplaySlot setUp (FrameDestination const&); + + + /** prepare and the given slot for output + * @param doAllocate allocate when true, else release it + * @throw lumiera::error::Logic when already in use */ + void allocate (LumieraDisplaySlot, bool doAllocate); + + + /** resolve the given display slot handle to yield a ref + * to an actual implementation object. In order to be resolvable, + * the DisplayerSlot needs to be locked (=allocated) for output use. */ + DisplayerSlot& resolve (LumieraDisplaySlot); + + }; + + + + + void + DisplayerSlot::put(LumieraDisplayFrame newFrame) + { + if (newFrame != currBuffer_) + { + currBuffer_ = newFrame; + dispatcher_.emit(); + } + else + { + TRACE (render, "frame dropped?"); + } + } + + + +} // namespace gui +#endif From cb6453afe1fce78bbbee3bb88d8c49b21df12a41 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 23 May 2011 05:46:40 +0200 Subject: [PATCH 010/296] detailed planning how to build the player subsystem key idea is to grow and rework the design of the DummyPlayer to yield the full featured Player --- src/backend/engine/scheduler-frontend.cpp | 9 +- src/backend/engine/scheduler-frontend.hpp | 21 +- src/proc/asset/viewer.cpp | 12 +- src/proc/asset/viewer.hpp | 29 +- src/proc/engine/dispatcher.cpp | 15 +- src/proc/engine/dispatcher.hpp | 28 +- src/proc/engine/engine-service.cpp | 359 +----------------- src/proc/engine/engine-service.hpp | 195 ++++------ .../builder/fixture-change-detector.hpp | 2 +- src/proc/mobject/output-designation.hpp | 4 +- src/proc/mobject/session/generator-mo.cpp | 61 +-- src/proc/mobject/session/generator-mo.hpp | 35 +- src/proc/play/dummy-play-connection.cpp | 358 +---------------- src/proc/play/dummy-play-connection.hpp | 69 ++++ src/proc/play/output-manager.cpp | 223 +---------- src/proc/play/output-manager.hpp | 160 ++------ src/proc/play/play-controller.hpp | 122 +----- src/proc/play/play-process.cpp | 355 +---------------- src/proc/play/play-process.hpp | 123 +----- src/proc/play/play-service.cpp | 292 +------------- src/proc/play/play-service.hpp | 115 +----- src/proc/play/sound/jack-output.cpp | 237 +----------- src/proc/play/sound/jack-output.hpp | 175 +-------- wiki/renderengine.html | 138 +++++-- 24 files changed, 472 insertions(+), 2665 deletions(-) create mode 100644 src/proc/play/dummy-play-connection.hpp diff --git a/src/backend/engine/scheduler-frontend.cpp b/src/backend/engine/scheduler-frontend.cpp index 588d1276b..3cf1698ab 100644 --- a/src/backend/engine/scheduler-frontend.cpp +++ b/src/backend/engine/scheduler-frontend.cpp @@ -1,8 +1,8 @@ /* - RenderEngine - a complete network of processing nodes usable for rendering + SchedulerFrontend - access point to the scheduler within the renderengine Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, 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 @@ -21,8 +21,9 @@ * *****************************************************/ -#include "proc/engine/renderengine.hpp" +#include "backend/engine/scheduler-frontend.hpp" +namespace backend{ namespace engine { @@ -30,4 +31,4 @@ namespace engine { -} // namespace engine +}} // namespace backend::engine diff --git a/src/backend/engine/scheduler-frontend.hpp b/src/backend/engine/scheduler-frontend.hpp index c3030271f..887435191 100644 --- a/src/backend/engine/scheduler-frontend.hpp +++ b/src/backend/engine/scheduler-frontend.hpp @@ -1,8 +1,8 @@ /* - RENDERENGINE.hpp - a complete network of processing nodes usable for rendering + SCHEDULER-FRONTEND.hpp - access point to the scheduler within the renderengine Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, 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 @@ -21,17 +21,15 @@ */ -#ifndef ENGINE_RENDERENGINE_H -#define ENGINE_RENDERENGINE_H - -#include - -#include "proc/engine/rendergraph.hpp" +#ifndef BACKEND_ENGINE_SCHEDULER_FRONTEND_H +#define BACKEND_ENGINE_SCHEDULER_FRONTEND_H -using std::list; + +//using std::list; +namespace backend{ namespace engine { @@ -42,7 +40,7 @@ namespace engine { * render operations are mostly implemented by the backend * ////////TODO WIP as of 12/2010 */ - class RenderEngine : public RenderGraph + class SchedulerFrontend { public: ///// TODO: find out about the public operations @@ -50,9 +48,8 @@ namespace engine { // but is a subsystem separate of the sesison. private: - list renderSegments; }; -} // namespace engine +}} // namespace backend::engine #endif diff --git a/src/proc/asset/viewer.cpp b/src/proc/asset/viewer.cpp index bb2f9e36e..b5c74ad30 100644 --- a/src/proc/asset/viewer.cpp +++ b/src/proc/asset/viewer.cpp @@ -1,8 +1,8 @@ /* - Timeline - independent top-level element of the Session + Viewer - asset corresponding to a viewer element in the GUI Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -21,17 +21,17 @@ * *****************************************************/ -#include "proc/asset/timeline.hpp" +#include "proc/asset/viewer.hpp" //#include "proc/mobject/session/track.hpp" //#include "proc/mobject/placement.hpp" //#include "proc/mobject/session/mobjectfactory.hpp" -#include "proc/mobject/session/binding.hpp" -#include "proc/assetmanager.hpp" +//#include "proc/mobject/session/binding.hpp" +//#include "proc/assetmanager.hpp" namespace asset { - using lib::AutoRegistered; +// using lib::AutoRegistered; diff --git a/src/proc/asset/viewer.hpp b/src/proc/asset/viewer.hpp index 7bf168e1e..5e5890552 100644 --- a/src/proc/asset/viewer.hpp +++ b/src/proc/asset/viewer.hpp @@ -1,8 +1,8 @@ /* - TIMELINE.hpp - independent top-level element of the Session + VIEWER.hpp - asset corresponding to a viewer element in the GUI Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -21,25 +21,8 @@ */ -/** @file timeline.hpp - ** Top level structural element within the session. - ** Each Lumiera session may contain multiple top level timeline containers, - ** which at the same time act as structural asset and as part of the public - ** session API exposed to clients for discovering the session contents. - ** Actually, Timelines are facade objects, delegating the implementation to - ** the BindingMO, the Axis and the Sequences/Tracks. - ** - ** Contrary to usual habits in video/sound editing software, in Lumiera the - ** tracks are \em not part of the timeline, but rather attached directly to - ** the sequence container. To be usable, a timeline needs a binding to refer - ** to such a sequence, but this sequence may be bound into multiple timelines - ** or even virtual clips simultaneously. - ** - ** Like every structural asset, the creation of timelines happens automatically - ** on referral; Timelines can be queried from the StructFactory, providing additional - ** requested capabilities. Commonly clients will retrieve a given timeline by query - ** on the name-ID of the timeline: \c Struct::retrieve(Query("id(theName).")) - ** Additionally, the binding to a specific sequence may be established alongside: +/** @file viewer.hpp + ** structural element within the session. ** \c "timeline(theTimelineName),bindSequence(theTimelineName,sequenceID)." ** ** @see Session @@ -49,8 +32,8 @@ */ -#ifndef ASSET_TIMELINE_H -#define ASSET_TIMELINE_H +#ifndef ASSET_VIEWER_H +#define ASSET_VIEWER_H #include "proc/asset/struct.hpp" //#include "proc/mobject/mobject.hpp" diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp index fefaf7c63..795b56c68 100644 --- a/src/proc/engine/dispatcher.cpp +++ b/src/proc/engine/dispatcher.cpp @@ -1,8 +1,8 @@ /* - RenderGraph - render network corresponding to one segment of the timeline + Dispatcher - translating calculation streams into frame jobs Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, 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 @@ -21,15 +21,10 @@ * *****************************************************/ -#include "proc/engine/rendergraph.hpp" -#include "lib/frameid.hpp" -#include "proc/state.hpp" +#include "proc/engine/dispatcher.hpp" +//#include "lib/frameid.hpp" +//#include "proc/state.hpp" -namespace lumiera { - - /** storage for the unique node-ID counter */ - ulong NodeID::currID (0); -} namespace engine { diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp index a77eac68b..5b4509953 100644 --- a/src/proc/engine/dispatcher.hpp +++ b/src/proc/engine/dispatcher.hpp @@ -1,8 +1,8 @@ /* - RENDERGRAPH.hpp - render network corresponding to one segment of the timeline + DISPATCHER.hpp - translating calculation streams into frame jobs Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, 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 @@ -21,8 +21,8 @@ */ -#ifndef ENGINE_RENDERGRAPH_H -#define ENGINE_RENDERGRAPH_H +#ifndef PROC_ENGINE_DISPATCHER_H +#define PROC_ENGINE_DISPATCHER_H #include "proc/common.hpp" #include "proc/state.hpp" @@ -32,28 +32,26 @@ namespace engine { - using lib::time::TimeSpan; - using lib::time::FSecs; - using lib::time::Time; - - class ExitNode; +// using lib::time::TimeSpan; +// using lib::time::FSecs; +// using lib::time::Time; +// +// class ExitNode; /** - * @todo likely to be reworked into the engine backbone /////////////TODO WIP as of 12/2010 + * @todo */ - class RenderGraph + class Dispatcher { protected: - ExitNode * output; - /** timerange covered by this RenderGraph */ TimeSpan segment_; public: - RenderGraph() + Dispatcher() : segment_(Time::ZERO, FSecs(5)) { - UNIMPLEMENTED ("anything regarding the Fixture datastructure"); + UNIMPLEMENTED ("anything regarding the Engine backbone"); } }; diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp index ea443f709..23078f68c 100644 --- a/src/proc/engine/engine-service.cpp +++ b/src/proc/engine/engine-service.cpp @@ -1,8 +1,8 @@ /* - DummyPlayerService - access point and service implementing a dummy test player + EngineService - primary service access point for using the renderengine Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -21,362 +21,33 @@ * *****************************************************/ -#include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" -#include "lib/singleton.hpp" +#include "proc/engine/engine-service.hpp" -extern "C" { -#include "common/interfacedescriptor.h" -} - -#include -#include -#include -#include +//#include +//#include +//#include +//#include namespace proc { - namespace play{ +namespace engine{ - using std::string; - using lumiera::Subsys; - using std::auto_ptr; - using boost::scoped_ptr; - using std::tr1::bind; +// using std::string; +// using lumiera::Subsys; +// using std::auto_ptr; +// using boost::scoped_ptr; +// using std::tr1::bind; namespace { // hidden local details of the service implementation.... - - /** details of how the DummyPlayer service can be started - * and used as independent "subsystem" within main() */ - class DummyPlayerSubsysDescriptor - : public Subsys - { - operator string () const { return "Dummy-Player"; } - - - bool - shouldStart (lumiera::Option&) - { - return false; // for now the DummyPlayerService only comes "up" as dependency, - } // but doesn't start as a subsystem on it's own. - - bool - start (lumiera::Option&, Subsys::SigTerm terminationHandle) - { - ASSERT (!thePlayer_); - - thePlayer_.reset (new DummyPlayerService (terminationHandle)); - return true; - } - - /** manages the actual (single) instance of the player service impl */ - scoped_ptr thePlayer_; - - - void - triggerShutdown () throw() - { - thePlayer_.reset(0); - // note: shutdown of the DummyPlayerService instance may block - // for a short period, until termination of all tick services - } - - bool - checkRunningState () throw() - { - return (thePlayer_); - } - }; - - lib::Singleton theDummyPlayerDescriptor; - - - - - - /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DummyPlayerFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", - const char*, (LumieraInterface ifa), - { (void)ifa; return "DummyPlayer"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", - const char*, (LumieraInterface ifa), - { - (void)ifa; - 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_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... - - typedef ProcessImpl* ProcP; - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 - ,lumieraorg_DummyPlayerService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", - LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return 0; - } - - return static_cast (_instance->start(viewerHandle)); - } - ) - , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", - void, (LumieraPlayProcess handle, bool doPlay), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - proc->doPlay(doPlay); - } - ) - , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", - void, (LumieraPlayProcess handle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - ProcessImpl::terminate (proc); - } - ) - ); - - - } // (End) hidden service impl details - DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) - : error_("") - , notifyTermination_(terminationHandle) - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) - { - INFO (progress, "DummyPlayer Facade opened."); - } - - - - - /** @par implementation note - * A new process (implementation) is created, configured - * and started here. This may include spawning a thread or - * allocating a timer. The newly created process is self-contained - * and will be just handed out, without caring for its lifecycle. - * If client code accesses this function via the plain C interface, - * the client is responsible for terminating this process, whereas - * when using the C++ interface, you'll get a Handle object which - * manages the lifecycle automatically. - */ - ProcessImpl* - DummyPlayerService::start (LumieraDisplaySlot viewerHandle) - { - auto_ptr newProcess (new ProcessImpl (viewerHandle)); - - REQUIRE (!newProcess->isActive()); - newProcess->setRate(25); - - return newProcess.release(); - } - - - - - - - /* === Process Implementation === */ - - - ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) - : fps_(0) - , play_(false) - , display_(Display::facade().getHandle (viewerHandle)) - , imageGen_(0) - , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) - { } - - - ProcessImpl::~ProcessImpl() - { - INFO (proc_dbg, "Playback process halted..."); - } - - - void - ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle - { - if (process) - delete process; - } - - - - DummyPlayer::Process - ProcessImpl::createHandle() - { - DummyPlayer::Process handle; - handle.activate(this, &terminate); // note the deleter function... - return handle; - } - - - - void - ProcessImpl::setRate (uint fps) - { - REQUIRE (fps==0 || fps_==0 ); - REQUIRE (fps==0 || !play_ ); - REQUIRE (tick_); - - fps_ = fps; - play_ = (fps != 0); - - if (play_) - imageGen_.reset(new DummyImageGenerator(fps)); - - // callbacks with given frequency, starting now - tick_->activate(fps); - } - - - - void - ProcessImpl::doPlay(bool yes) - { - REQUIRE (isActive()); - tick_->activate (yes? fps_:0); - play_ = yes; - } - - - - void - ProcessImpl::doFrame() - { - REQUIRE (isActive()); - ASSERT (imageGen_); - - if (play_) - display_(imageGen_->next()); - else - display_(imageGen_->current()); - } + /** */ - - } // namespace play - -} // namespace proc - - - - - -namespace lumiera { /* === Forwarding function(s) on the Process handle === */ - - void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } - - - - - - /** @internal intended for use by main(). */ - lumiera::Subsys& - DummyPlayer::getDescriptor() - { - return proc::play::theDummyPlayerDescriptor(); - } - - // emit the vtable here into this translation unit within liblumieraproc.so ... - DummyPlayer::~DummyPlayer() { } - - -} // namespace lumiera +}} // namespace proc::engine diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index 75e0f3d78..599f4fe40 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -1,8 +1,8 @@ /* - DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + ENGINE-SERVICE.hpp - primary service access point for using the renderengine Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -20,142 +20,83 @@ */ -/** @file dummy-player-service.hpp - ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. - ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The - ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding - ** the necessary handles and allocations and providing an uniform API to the client side. - ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and - ** it uses an output handle (functor) to push the generated frames up. - ** - ** This service is the implementation of a layer separation facade interface. Clients should use - ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used - ** to \em provide this service, not to access it. +/** @file engine-service.hpp + ** A public service provided by ** ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example */ -#ifndef PROC_DUMMYPLAYER_SERVICE_H -#define PROC_DUMMYPLAYER_SERVICE_H +#ifndef PROC_ENGINE_ENGINE_SERVICE_H +#define PROC_ENGINE_ENGINE_SERVICE_H -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" - -#include -#include -#include +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include +//#include +//#include namespace proc { - namespace play { - - using std::string; - using lumiera::Subsys; - using lumiera::Display; - using lumiera::DummyPlayer; - - - class DummyImageGenerator; - class TickService; - - - /******************************************************************** - * Actual implementation of a single (dummy) playback process. - * The DummyPlayerService (see below) maintains a collection of such - * actively running playback processes, while the client code gets - * DummyPlayer::Process handles to track any ongoing use. Users of - * the plain C interface get a direct bare pointer to the respective - * ProcessImpl instance and have to manage the lifecycle manually. - */ - class ProcessImpl - : public lumiera_playprocess, - boost::noncopyable - { - uint fps_; - bool play_; - - Display::Sink display_; - boost::scoped_ptr imageGen_; - boost::scoped_ptr tick_; - - - public: - ProcessImpl(LumieraDisplaySlot) ; - ~ProcessImpl() ; - - - /* Implementation-level API */ - - /** activate a playback process - * with given specification */ - void setRate (uint fps); - - bool isActive () { return fps_ != 0; } - bool isPlaying() { return play_; } - - void doPlay(bool yes); - - - /* Lifecycle */ - - DummyPlayer::Process createHandle(); - static void terminate(ProcessImpl* process); - - private: - void doFrame (); ///< periodically invoked while playing - }; - - - - /****************************************************** - * Actual implementation of the DummyPlayer service. - * Creating an instance of this class automatically - * registers the interface lumieraorg_DummyPlayer with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - */ - class DummyPlayerService - : boost::noncopyable - { - - string error_; - Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - - public: - DummyPlayerService(Subsys::SigTerm terminationHandle); - - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); - - }; - - - - - } // namespace play +namespace play { +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + + + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class EngineService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + +} // namespace play } // namespace proc #endif diff --git a/src/proc/mobject/builder/fixture-change-detector.hpp b/src/proc/mobject/builder/fixture-change-detector.hpp index bf836d7d1..e2da29419 100644 --- a/src/proc/mobject/builder/fixture-change-detector.hpp +++ b/src/proc/mobject/builder/fixture-change-detector.hpp @@ -30,7 +30,7 @@ ** and specific segments of the fixture. ** Together, these allow to identify those ongoing processes which need to be cancelled ** or restarted, because their results might be tainted by the changes induced by the - ** build process. Typically, these detection process runs just before commiting the + ** build process. Typically, these detection process runs just before committing the ** newly built fixture datastructure. ** ** @todo WIP-WIP-WIP as of 12/2010 diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp index 43aa6f5b8..fd571e56a 100644 --- a/src/proc/mobject/output-designation.hpp +++ b/src/proc/mobject/output-designation.hpp @@ -46,7 +46,7 @@ namespace mobject { /** * Descriptor to denote the desired target of produced media data. * OutputDesignation is always an internal and relative specification - * and boils down to referring an asset::Pipe by ID. In order to get + * and boils down to referring an asset::Pipe by ID. In order to become * actually effective, some object within the model additionally * needs to \em claim this pipe-ID, meaning that this object * states to root and represent this pipe. When the builder @@ -54,7 +54,7 @@ namespace mobject { * an actual stream connection will be wired in the * processing node network. * - * @todo couldn't the inline buffer be "downgraded" to InPlaceBuffer ?? + * @todo couldn't the inline buffer be "downgraded" to InPlaceBuffer or PolymorphicValue?? * Seemingly we never-ever need to re-discover the erased type of the embedded spec. * Thus for this to work, we'd just need to add an "empty" spec ///////////////////TICKET #723 */ diff --git a/src/proc/mobject/session/generator-mo.cpp b/src/proc/mobject/session/generator-mo.cpp index 426cd8e57..728e58b94 100644 --- a/src/proc/mobject/session/generator-mo.cpp +++ b/src/proc/mobject/session/generator-mo.cpp @@ -1,8 +1,8 @@ /* - Clip - a Media Clip + GeneratorMO - a (Test)data generator Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, 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 @@ -21,15 +21,15 @@ * *****************************************************/ -#include "proc/mobject/session/clip.hpp" -#include "proc/assetmanager.hpp" -#include "proc/asset/media.hpp" -#include "proc/asset/clip.hpp" -#include "lib/time/mutation.hpp" -#include "lib/util.hpp" +#include "proc/mobject/session/generator-mo.hpp" +//#include "proc/assetmanager.hpp" +//#include "proc/asset/media.hpp" +//#include "proc/asset/clip.hpp" +//#include "lib/time/mutation.hpp" +//#include "lib/util.hpp" -using lib::time::Mutation; -using util::isnil; +//using lib::time::Mutation; +//using util::isnil; namespace mobject { namespace session { @@ -37,47 +37,6 @@ namespace session { /** new clip-MO linked with the given asset::Clip. * Initially, this clip will cover the whole source media length. */ - Clip::Clip (const asset::Clip& clipDef, const Media& mediaDef) - : mediaDef_(mediaDef) - , clipDef_(clipDef) - { - setupLength(); - throwIfInvalid(); - } - - - - /** implementing the common MObject self test. - * Length definition is consitent, underlying - * media def is accessible etc. */ - bool - Clip::isValid () const - { - TODO ("check consistency of clip length def, implies accessing the underlying media def"); - return !isnil(length_); - } - - - void - Clip::setupLength() - { - TODO ("really calculate the length of a clip and set length field"); - this->length_.accept (Mutation::changeDuration(mediaDef_.getLength())); - } - - - PMedia - Clip::getMedia () const - { - return asset::AssetManager::wrap (mediaDef_); - } - - - PClipAsset - Clip::findClipAsset () const - { - return asset::AssetManager::wrap (clipDef_); - } diff --git a/src/proc/mobject/session/generator-mo.hpp b/src/proc/mobject/session/generator-mo.hpp index 52e134926..876a497f3 100644 --- a/src/proc/mobject/session/generator-mo.hpp +++ b/src/proc/mobject/session/generator-mo.hpp @@ -1,8 +1,8 @@ /* - CLIP.hpp - a Media Clip + GENERATOR-MO.hpp - a (Test)data generator Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, 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 @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_CLIP_H -#define MOBJECT_SESSION_CLIP_H +#ifndef MOBJECT_SESSION_GENERATOR_MO_H +#define MOBJECT_SESSION_GENERATOR_MO_H #include "proc/mobject/session/abstractmo.hpp" #include "lib/time/timevalue.hpp" @@ -44,24 +44,15 @@ namespace session { /** - * A user visible/editable Clip is a reference to a contiguous - * sequence of media data loaded as Asset into the current Session. - * As such, it is a virtual (non destructive) cut or edit of the - * source material and can be placed into the Session to be rendered - * into the output. The actual media type of a clip will be derived - * at runtime by resolving this reference to the underlying Asset. - * - * @todo define how to denote Time positions /lengths. This is tricky, - * because it depends on the actual media type, and we want to encapsulate - * all these details as much as possible. + * A lksjaf */ - class Clip + class GeneratorMO : public AbstractMO { string initShortID() const { - return buildShortID("Clip"); + return buildShortID("Generator"); } void setupLength(); @@ -79,24 +70,14 @@ namespace session { account when breaking circular references. */ - const Media & mediaDef_; - const asset::Clip & clipDef_; - Clip (const asset::Clip&, const Media&); + GeneratorMO (); friend class MObjectFactory; public: bool isValid() const; - /** access the underlying media asset */ - PMedia getMedia () const; - - /** locate the corresponding asset - * representing this clip or the whole - * compound in case of a multichannel clip - */ - PClipAsset findClipAsset () const; DEFINE_PROCESSABLE_BY (builder::BuilderTool); diff --git a/src/proc/play/dummy-play-connection.cpp b/src/proc/play/dummy-play-connection.cpp index ea443f709..72dab3d41 100644 --- a/src/proc/play/dummy-play-connection.cpp +++ b/src/proc/play/dummy-play-connection.cpp @@ -1,8 +1,8 @@ /* - DummyPlayerService - access point and service implementing a dummy test player + DummyPlayConnection.hpp - simplified test setup for playback Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -21,362 +21,38 @@ * *****************************************************/ -#include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" -#include "lib/singleton.hpp" +#include "proc/play/dummy-play-connection.hpp" +//#include "proc/play/dummy-image-generator.hpp" +//#include "proc/play/tick-service.hpp" +//#include "lib/singleton.hpp" -extern "C" { -#include "common/interfacedescriptor.h" -} - -#include -#include -#include -#include +//#include +//#include +//#include +//#include namespace proc { namespace play{ - using std::string; - using lumiera::Subsys; - using std::auto_ptr; - using boost::scoped_ptr; - using std::tr1::bind; +// using std::string; +// using lumiera::Subsys; +// using std::auto_ptr; +// using boost::scoped_ptr; +// using std::tr1::bind; namespace { // hidden local details of the service implementation.... - - /** details of how the DummyPlayer service can be started - * and used as independent "subsystem" within main() */ - class DummyPlayerSubsysDescriptor - : public Subsys - { - operator string () const { return "Dummy-Player"; } - - - bool - shouldStart (lumiera::Option&) - { - return false; // for now the DummyPlayerService only comes "up" as dependency, - } // but doesn't start as a subsystem on it's own. - - bool - start (lumiera::Option&, Subsys::SigTerm terminationHandle) - { - ASSERT (!thePlayer_); - - thePlayer_.reset (new DummyPlayerService (terminationHandle)); - return true; - } - - /** manages the actual (single) instance of the player service impl */ - scoped_ptr thePlayer_; - - - void - triggerShutdown () throw() - { - thePlayer_.reset(0); - // note: shutdown of the DummyPlayerService instance may block - // for a short period, until termination of all tick services - } - - bool - checkRunningState () throw() - { - return (thePlayer_); - } - }; - - lib::Singleton theDummyPlayerDescriptor; - - - - - - /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DummyPlayerFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", - const char*, (LumieraInterface ifa), - { (void)ifa; return "DummyPlayer"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", - const char*, (LumieraInterface ifa), - { - (void)ifa; - 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_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... - - typedef ProcessImpl* ProcP; - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 - ,lumieraorg_DummyPlayerService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", - LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return 0; - } - - return static_cast (_instance->start(viewerHandle)); - } - ) - , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", - void, (LumieraPlayProcess handle, bool doPlay), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - proc->doPlay(doPlay); - } - ) - , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", - void, (LumieraPlayProcess handle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - ProcessImpl::terminate (proc); - } - ) - ); - - - } // (End) hidden service impl details - DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) - : error_("") - , notifyTermination_(terminationHandle) - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) - { - INFO (progress, "DummyPlayer Facade opened."); - } - - - - - /** @par implementation note - * A new process (implementation) is created, configured - * and started here. This may include spawning a thread or - * allocating a timer. The newly created process is self-contained - * and will be just handed out, without caring for its lifecycle. - * If client code accesses this function via the plain C interface, - * the client is responsible for terminating this process, whereas - * when using the C++ interface, you'll get a Handle object which - * manages the lifecycle automatically. - */ - ProcessImpl* - DummyPlayerService::start (LumieraDisplaySlot viewerHandle) - { - auto_ptr newProcess (new ProcessImpl (viewerHandle)); - - REQUIRE (!newProcess->isActive()); - newProcess->setRate(25); - - return newProcess.release(); - } - - - - - - - /* === Process Implementation === */ - - - ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) - : fps_(0) - , play_(false) - , display_(Display::facade().getHandle (viewerHandle)) - , imageGen_(0) - , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) - { } - - - ProcessImpl::~ProcessImpl() - { - INFO (proc_dbg, "Playback process halted..."); - } - - - void - ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle - { - if (process) - delete process; - } - - - - DummyPlayer::Process - ProcessImpl::createHandle() - { - DummyPlayer::Process handle; - handle.activate(this, &terminate); // note the deleter function... - return handle; - } - - - - void - ProcessImpl::setRate (uint fps) - { - REQUIRE (fps==0 || fps_==0 ); - REQUIRE (fps==0 || !play_ ); - REQUIRE (tick_); - - fps_ = fps; - play_ = (fps != 0); - - if (play_) - imageGen_.reset(new DummyImageGenerator(fps)); - - // callbacks with given frequency, starting now - tick_->activate(fps); - } - - - - void - ProcessImpl::doPlay(bool yes) - { - REQUIRE (isActive()); - tick_->activate (yes? fps_:0); - play_ = yes; - } - - - - void - ProcessImpl::doFrame() - { - REQUIRE (isActive()); - ASSERT (imageGen_); - - if (play_) - display_(imageGen_->next()); - else - display_(imageGen_->current()); - } - - + /** */ + } // namespace play } // namespace proc - - - - - -namespace lumiera { /* === Forwarding function(s) on the Process handle === */ - - void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } - - - - - - /** @internal intended for use by main(). */ - lumiera::Subsys& - DummyPlayer::getDescriptor() - { - return proc::play::theDummyPlayerDescriptor(); - } - - // emit the vtable here into this translation unit within liblumieraproc.so ... - DummyPlayer::~DummyPlayer() { } - - -} // namespace lumiera diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp new file mode 100644 index 000000000..7186987df --- /dev/null +++ b/src/proc/play/dummy-play-connection.hpp @@ -0,0 +1,69 @@ +/* + DUMMY-PLAY-CONNECTION.hpp - simplified test setup for playback + + Copyright (C) Lumiera.org + 2011, 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 dummy-play-connection.hpp + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_PLAY_DUMMY_PLAY_CONNECTION_H +#define PROC_PLAY_DUMMY_PLAY_CONNECTION_H + + +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include +//#include +//#include + + +namespace proc { + namespace play { + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + + + + /******************************************************************** + */ + class DummyPlayConnection + : boost::noncopyable + { + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/output-manager.cpp b/src/proc/play/output-manager.cpp index 96600bf3b..39887ec80 100644 --- a/src/proc/play/output-manager.cpp +++ b/src/proc/play/output-manager.cpp @@ -1,8 +1,8 @@ /* - DisplayService - service providing access to a display for outputting frames + OutputManager - handling all the real external output connections Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -21,232 +21,23 @@ * *****************************************************/ -#include "gui/display-service.hpp" - -extern "C" { -#include "common/interfacedescriptor.h" -} +#include "play/output-manager.hpp" -namespace gui { + +namespace play { namespace { // hidden local details of the service implementation.... - - - /* ================== define an lumieraorg_Display instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DisplayFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\323\343\324\023\064\216\120\201\073\056\366\020\110\263\060\023", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Display"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\305\026\070\133\033\357\014\202\203\270\174\072\341\256\226\235", - const char*, (LumieraInterface ifa), - { (void)ifa; return "UI Interface: service for outputting frames to a viewer or display"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\170\104\246\175\123\144\332\312\315\263\071\170\164\213\024\275", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\265\343\045\346\110\241\276\111\217\120\155\246\230\341\344\124", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\302\027\122\045\301\166\046\236\257\253\144\035\105\166\070\103", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\074\013\020\161\075\135\302\265\260\000\301\147\116\355\035\261", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\037\232\153\100\114\103\074\342\164\132\370\210\372\164\115\275", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\026\243\334\056\125\245\315\311\155\375\262\344\007\076\341\254", - const char*, (LumieraInterface ifa), - { - (void)ifa; - 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_INTERFACE_INLINE (state, "\243\302\332\160\060\272\155\334\212\256\303\141\160\063\164\154", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\363\125\123\060\231\147\053\017\131\341\105\157\231\273\334\136", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DisplayService implementation... - - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_Display, 0 - ,lumieraorg_DisplayService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DisplayFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (allocate, "\177\221\146\253\255\161\160\137\015\005\263\362\307\022\243\365", - void, (LumieraDisplaySlot slotHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (slotHandle); - try - { - _instance->allocate (slotHandle,true); - } - catch (lumiera::Error&){ /* error state remains set */ } - } - ) - , LUMIERA_INTERFACE_INLINE (release, "\166\374\106\313\011\142\115\161\111\110\376\016\346\115\240\364", - void, (LumieraDisplaySlot slotHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (slotHandle); - _instance->allocate (slotHandle,false); - } - ) - , LUMIERA_INTERFACE_INLINE (put, "\340\062\234\227\152\131\370\272\146\207\224\015\361\070\252\135", - void, (LumieraDisplaySlot slotHandle, LumieraDisplayFrame frame), - { - //skipping full checks for performance reasons - REQUIRE (_instance && !lumiera_error_peek()); - - REQUIRE (slotHandle); - DisplayerSlot& slot = _instance->resolve (slotHandle); - slot.put (frame); - } - ) - ); - - - - } // (End) hidden service impl details - DisplayService::DisplayService() - : error_("") - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_Display, 0, lumieraorg_DisplayService)) - { - INFO (progress, "Display Facade opened."); - } + OutputSlot::~OutputSlot() { } - LumieraDisplaySlot - DisplayService::setUp (FrameDestination const& outputDestination) - { - DisplayerTab& slots (_instance->slots_); - return &slots.manage (new DisplayerSlot (outputDestination)); - } - - - - void - DisplayService::allocate (LumieraDisplaySlot handle, bool doAllocate) - { - REQUIRE (handle); - if (doAllocate) - { - if (handle->put_) - throw lumiera::error::Logic("slot already allocated for output"); - else - // Mark the handle as "allocated" and ready for output: - // Place the function pointer from the C interface into the handle struct. - // calling it will invoke the implementing instance's "put" function - // (see the LUMIERA_INTERFACE_INLINE above in this file!) - handle->put_ = serviceInstance_.get().put; - } - else - handle->put_ = 0; - } - - - - DisplayerSlot& - DisplayService::resolve (LumieraDisplaySlot handle) - { - REQUIRE (handle); - REQUIRE (handle->put_, "accessing a DisplayerSlot, which hasn't been locked for output"); - - return *static_cast (handle); - } - - - - - - /* === DisplayerSlot Implementation === */ - - - DisplayerSlot::DisplayerSlot (FrameDestination const& outputDestination) - : currBuffer_(0) - { - put_ = 0; // mark as not allocated - hasFrame_.connect (outputDestination); - dispatcher_.connect (sigc::mem_fun (this, &DisplayerSlot::displayCurrentFrame)); - } - - - DisplayerSlot::~DisplayerSlot() - { - TRACE (gui_dbg, "Displayer Slot closing..."); - } - - - void - DisplayerSlot::displayCurrentFrame() - { - hasFrame_.emit (currBuffer_); - } - - -} // namespace proc +} // namespace play diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index 1dec2c192..b697f35d6 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -1,8 +1,8 @@ /* - DISPLAY-SERVICE.hpp - service providing access to a display for outputting frames + OUTPUT-MANAGER.hpp - handling all the real external output connections Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -20,171 +20,65 @@ */ -/** @file display-service.hpp - ** A public service provided by the GUI, implementing the lumiera::Display facade interface. - ** It serves two purposes: - ** - It maintains a collection of DisplayerSlot objects, which are the actual connection points - ** and allow to receive frames and dispatch them to the GTK main event loop thread. - ** Conceptually, creating such a slot means providing a possible display for output. - ** - It provides the actual implementation of the Display facade interface, i.e. the function - ** which is to invoked periodically by the playback processes to dispose a new frame into - ** the display. - ** - ** This service is the implementation of a layer separation facade interface. This header defines - ** the interface used to \em provide this service, not to access it. Clients get a specific - ** LumieraDisplaySlot passed as parameter when initiating a playback process from the GUI. Using - ** this LumieraDisplaySlot handle, clients should then use lumiera::DummyPlayer#facade to access - ** an implementation instance of this service in order to push actual frames up. +/** @file output-manager.hpp + ** A global service to handle all external output connections. ** - ** @see lumiera::Display - ** @see lumiera::DummyPlayer - ** @see gui::PlaybackController usage example + ** @see output-manager-test.cpp ////TODO */ -#ifndef GUI_DISPLAY_SERVICE_H -#define GUI_DISPLAY_SERVICE_H +#ifndef PROC_PLAY_OUTPUT_MANAGER_H +#define PROC_PLAY_OUTPUT_MANAGER_H -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" -#include "lib/scoped-ptrvect.hpp" +#include "lib/error.hpp" -#include -#include #include -#include -#include +//#include +//#include -namespace gui { +namespace play { - using std::string; - using std::vector; - using lumiera::Display; - using Glib::Dispatcher; +//using std::string; +//using std::vector; - typedef sigc::slot FrameDestination; - typedef sigc::signal FrameSignal; /******************************************************************** - * Actual implementation of a single displayer slot. Internally, - * it is connected via the Glib::Dispatcher for outputting frames - * to a viewer widget, which executes within the GTK event thread. - * @note must be created from the GTK event thread. + * Interface: Generic output sink. + * + * @todo write type comment */ - class DisplayerSlot - : public lumiera_displaySlot, - boost::noncopyable + class OutputSlot + : boost::noncopyable { - Dispatcher dispatcher_; - FrameSignal hasFrame_; - - LumieraDisplayFrame currBuffer_; - - public: - DisplayerSlot (FrameDestination const&) ; - ~DisplayerSlot () ; - - /* Implementation-level API to be used by DisplayService */ - - /** receive a frame to be displayed */ - inline void put (LumieraDisplayFrame); - + virtual ~OutputSlot(); private: - /** internal: activated via Dispatcher - * and running in GTK main thread */ - void displayCurrentFrame(); }; - typedef lib::ScopedPtrVect DisplayerTab; +//typedef lib::ScopedPtrVect DisplayerTab; /****************************************************** - * Actual implementation of the DisplayService. - * Creating an instance of this class automatically - * registers the interface lumieraorg_Display with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - * \par - * In addition to the Display interface, this class - * implements an additional service for the GUI, - * allowing actually to set up display slots, which - * then can be handed out to client code in the - * course of the play process for outputting frames. + * Management of external Output connections. + * + * @todo write Type comment */ - class DisplayService + class OutputManager : boost::noncopyable { - - string error_; - DisplayerTab slots_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_Display, 0) - , lumiera::Display - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - - public: - DisplayService(); - ~DisplayService() { - INFO (proc_dbg, "Display service dying..."); - - } - - - /** open a new display, sending frames to the given output destination - * @return handle for this slot, can be used to start a play process. - * NULL handle in case of any error. */ - static LumieraDisplaySlot setUp (FrameDestination const&); - - - /** prepare and the given slot for output - * @param doAllocate allocate when true, else release it - * @throw lumiera::error::Logic when already in use */ - void allocate (LumieraDisplaySlot, bool doAllocate); - - - /** resolve the given display slot handle to yield a ref - * to an actual implementation object. In order to be resolvable, - * the DisplayerSlot needs to be locked (=allocated) for output use. */ - DisplayerSlot& resolve (LumieraDisplaySlot); - + OutputManager() {} }; + - - - void - DisplayerSlot::put(LumieraDisplayFrame newFrame) - { - if (newFrame != currBuffer_) - { - currBuffer_ = newFrame; - dispatcher_.emit(); - } - else - { - TRACE (render, "frame dropped?"); - } - } - - - -} // namespace gui +} // namespace play #endif diff --git a/src/proc/play/play-controller.hpp b/src/proc/play/play-controller.hpp index 75e0f3d78..239ffa0c5 100644 --- a/src/proc/play/play-controller.hpp +++ b/src/proc/play/play-controller.hpp @@ -1,8 +1,8 @@ /* - DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + PLAY-CONTROLLER.hpp - frontend handle to control an play process Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -20,141 +20,49 @@ */ -/** @file dummy-player-service.hpp - ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. - ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The - ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding - ** the necessary handles and allocations and providing an uniform API to the client side. - ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and - ** it uses an output handle (functor) to push the generated frames up. - ** - ** This service is the implementation of a layer separation facade interface. Clients should use - ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used - ** to \em provide this service, not to access it. +/** @file play-controller.hpp ** ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example */ -#ifndef PROC_DUMMYPLAYER_SERVICE_H -#define PROC_DUMMYPLAYER_SERVICE_H +#ifndef PROC_PLAY_PLAY_CONTROLLER_H +#define PROC_PLAY_PLAY_CONTROLLER_H -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" +#include "proc/play/play-service.hpp" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" #include -#include -#include +//#include +//#include namespace proc { namespace play { using std::string; - using lumiera::Subsys; - using lumiera::Display; - using lumiera::DummyPlayer; - - - class DummyImageGenerator; - class TickService; /******************************************************************** - * Actual implementation of a single (dummy) playback process. - * The DummyPlayerService (see below) maintains a collection of such - * actively running playback processes, while the client code gets - * DummyPlayer::Process handles to track any ongoing use. Users of - * the plain C interface get a direct bare pointer to the respective - * ProcessImpl instance and have to manage the lifecycle manually. */ - class ProcessImpl - : public lumiera_playprocess, - boost::noncopyable - { - uint fps_; - bool play_; - - Display::Sink display_; - boost::scoped_ptr imageGen_; - boost::scoped_ptr tick_; - - - public: - ProcessImpl(LumieraDisplaySlot) ; - ~ProcessImpl() ; - - - /* Implementation-level API */ - - /** activate a playback process - * with given specification */ - void setRate (uint fps); - - bool isActive () { return fps_ != 0; } - bool isPlaying() { return play_; } - - void doPlay(bool yes); - - - /* Lifecycle */ - - DummyPlayer::Process createHandle(); - static void terminate(ProcessImpl* process); - - private: - void doFrame (); ///< periodically invoked while playing - }; - - - - /****************************************************** - * Actual implementation of the DummyPlayer service. - * Creating an instance of this class automatically - * registers the interface lumieraorg_DummyPlayer with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - */ - class DummyPlayerService + class PlayController : boost::noncopyable { - string error_; - Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - public: - DummyPlayerService(Subsys::SigTerm terminationHandle); - - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); + private: }; + + } // namespace play } // namespace proc diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index ea443f709..3859244a5 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -1,8 +1,8 @@ /* - DummyPlayerService - access point and service implementing a dummy test player + PlayProcess.hpp - state frame for an ongoing play/render process Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -21,212 +21,27 @@ * *****************************************************/ -#include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" -#include "lib/singleton.hpp" +#include "proc/play/play-process.hpp" -extern "C" { -#include "common/interfacedescriptor.h" -} - -#include -#include -#include -#include +//#include +//#include +//#include +//#include namespace proc { namespace play{ - using std::string; - using lumiera::Subsys; - using std::auto_ptr; - using boost::scoped_ptr; - using std::tr1::bind; +// using std::string; +// using lumiera::Subsys; +// using std::auto_ptr; +// using boost::scoped_ptr; +// using std::tr1::bind; namespace { // hidden local details of the service implementation.... - /** details of how the DummyPlayer service can be started - * and used as independent "subsystem" within main() */ - class DummyPlayerSubsysDescriptor - : public Subsys - { - operator string () const { return "Dummy-Player"; } - - - bool - shouldStart (lumiera::Option&) - { - return false; // for now the DummyPlayerService only comes "up" as dependency, - } // but doesn't start as a subsystem on it's own. - - bool - start (lumiera::Option&, Subsys::SigTerm terminationHandle) - { - ASSERT (!thePlayer_); - - thePlayer_.reset (new DummyPlayerService (terminationHandle)); - return true; - } - - /** manages the actual (single) instance of the player service impl */ - scoped_ptr thePlayer_; - - - void - triggerShutdown () throw() - { - thePlayer_.reset(0); - // note: shutdown of the DummyPlayerService instance may block - // for a short period, until termination of all tick services - } - - bool - checkRunningState () throw() - { - return (thePlayer_); - } - }; - - lib::Singleton theDummyPlayerDescriptor; - - - - - - /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DummyPlayerFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", - const char*, (LumieraInterface ifa), - { (void)ifa; return "DummyPlayer"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", - const char*, (LumieraInterface ifa), - { - (void)ifa; - 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_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... - - typedef ProcessImpl* ProcP; - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 - ,lumieraorg_DummyPlayerService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", - LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return 0; - } - - return static_cast (_instance->start(viewerHandle)); - } - ) - , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", - void, (LumieraPlayProcess handle, bool doPlay), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - proc->doPlay(doPlay); - } - ) - , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", - void, (LumieraPlayProcess handle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - ProcessImpl::terminate (proc); - } - ) - ); - - } // (End) hidden service impl details @@ -234,149 +49,7 @@ namespace proc { - DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) - : error_("") - , notifyTermination_(terminationHandle) - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) - { - INFO (progress, "DummyPlayer Facade opened."); - } - - - - - /** @par implementation note - * A new process (implementation) is created, configured - * and started here. This may include spawning a thread or - * allocating a timer. The newly created process is self-contained - * and will be just handed out, without caring for its lifecycle. - * If client code accesses this function via the plain C interface, - * the client is responsible for terminating this process, whereas - * when using the C++ interface, you'll get a Handle object which - * manages the lifecycle automatically. - */ - ProcessImpl* - DummyPlayerService::start (LumieraDisplaySlot viewerHandle) - { - auto_ptr newProcess (new ProcessImpl (viewerHandle)); - - REQUIRE (!newProcess->isActive()); - newProcess->setRate(25); - - return newProcess.release(); - } - - - - - - - /* === Process Implementation === */ - - - ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) - : fps_(0) - , play_(false) - , display_(Display::facade().getHandle (viewerHandle)) - , imageGen_(0) - , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) - { } - - - ProcessImpl::~ProcessImpl() - { - INFO (proc_dbg, "Playback process halted..."); - } - - - void - ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle - { - if (process) - delete process; - } - - - - DummyPlayer::Process - ProcessImpl::createHandle() - { - DummyPlayer::Process handle; - handle.activate(this, &terminate); // note the deleter function... - return handle; - } - - - - void - ProcessImpl::setRate (uint fps) - { - REQUIRE (fps==0 || fps_==0 ); - REQUIRE (fps==0 || !play_ ); - REQUIRE (tick_); - - fps_ = fps; - play_ = (fps != 0); - - if (play_) - imageGen_.reset(new DummyImageGenerator(fps)); - - // callbacks with given frequency, starting now - tick_->activate(fps); - } - - - - void - ProcessImpl::doPlay(bool yes) - { - REQUIRE (isActive()); - tick_->activate (yes? fps_:0); - play_ = yes; - } - - - - void - ProcessImpl::doFrame() - { - REQUIRE (isActive()); - ASSERT (imageGen_); - - if (play_) - display_(imageGen_->next()); - else - display_(imageGen_->current()); - } + /** */ - - } // namespace play - -} // namespace proc - - - - - -namespace lumiera { /* === Forwarding function(s) on the Process handle === */ - - void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } - - - - - - /** @internal intended for use by main(). */ - lumiera::Subsys& - DummyPlayer::getDescriptor() - { - return proc::play::theDummyPlayerDescriptor(); - } - - // emit the vtable here into this translation unit within liblumieraproc.so ... - DummyPlayer::~DummyPlayer() { } - - -} // namespace lumiera +}} // namespace proc::play diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 75e0f3d78..f9d0ea85c 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -1,8 +1,8 @@ /* - DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + PLAY-PROCESS.hpp - state frame for an ongoing play/render process Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -20,95 +20,34 @@ */ -/** @file dummy-player-service.hpp - ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. - ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The - ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding - ** the necessary handles and allocations and providing an uniform API to the client side. - ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and - ** it uses an output handle (functor) to push the generated frames up. - ** - ** This service is the implementation of a layer separation facade interface. Clients should use - ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used - ** to \em provide this service, not to access it. - ** +/** @file play-process.hpp + * ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example */ -#ifndef PROC_DUMMYPLAYER_SERVICE_H -#define PROC_DUMMYPLAYER_SERVICE_H +#ifndef PROC_PLAY_PLAY_PROCESS_H +#define PROC_PLAY_PLAY_PROCESS_H -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" - -#include -#include -#include +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include +//#include +//#include namespace proc { namespace play { - using std::string; - using lumiera::Subsys; - using lumiera::Display; - using lumiera::DummyPlayer; - - - class DummyImageGenerator; - class TickService; - - - /******************************************************************** - * Actual implementation of a single (dummy) playback process. - * The DummyPlayerService (see below) maintains a collection of such - * actively running playback processes, while the client code gets - * DummyPlayer::Process handles to track any ongoing use. Users of - * the plain C interface get a direct bare pointer to the respective - * ProcessImpl instance and have to manage the lifecycle manually. - */ - class ProcessImpl - : public lumiera_playprocess, - boost::noncopyable - { - uint fps_; - bool play_; - - Display::Sink display_; - boost::scoped_ptr imageGen_; - boost::scoped_ptr tick_; - - - public: - ProcessImpl(LumieraDisplaySlot) ; - ~ProcessImpl() ; - - - /* Implementation-level API */ - - /** activate a playback process - * with given specification */ - void setRate (uint fps); - - bool isActive () { return fps_ != 0; } - bool isPlaying() { return play_; } - - void doPlay(bool yes); - - - /* Lifecycle */ - - DummyPlayer::Process createHandle(); - static void terminate(ProcessImpl* process); - - private: - void doFrame (); ///< periodically invoked while playing - }; +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; @@ -120,35 +59,11 @@ namespace proc { * a forwarding proxy within the application core to * route calls through this interface. */ - class DummyPlayerService + class PlayerProcess : boost::noncopyable { - string error_; - Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - public: - DummyPlayerService(Subsys::SigTerm terminationHandle); - - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index ea443f709..1b8cc463e 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -1,8 +1,8 @@ /* - DummyPlayerService - access point and service implementing a dummy test player + PlayService - interface: render- and playback control Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -21,19 +21,14 @@ * *****************************************************/ -#include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" +#include "proc/play/play-service.hpp" #include "lib/singleton.hpp" -extern "C" { -#include "common/interfacedescriptor.h" -} #include -#include -#include -#include +//#include +//#include +//#include @@ -99,284 +94,13 @@ namespace proc { /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DummyPlayerFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", - const char*, (LumieraInterface ifa), - { (void)ifa; return "DummyPlayer"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", - const char*, (LumieraInterface ifa), - { - (void)ifa; - 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_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... - - typedef ProcessImpl* ProcP; - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 - ,lumieraorg_DummyPlayerService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", - LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return 0; - } - - return static_cast (_instance->start(viewerHandle)); - } - ) - , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", - void, (LumieraPlayProcess handle, bool doPlay), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - proc->doPlay(doPlay); - } - ) - , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", - void, (LumieraPlayProcess handle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - ProcessImpl::terminate (proc); - } - ) - ); - - - } // (End) hidden service impl details - DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) - : error_("") - , notifyTermination_(terminationHandle) - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) - { - INFO (progress, "DummyPlayer Facade opened."); - } - - - - - /** @par implementation note - * A new process (implementation) is created, configured - * and started here. This may include spawning a thread or - * allocating a timer. The newly created process is self-contained - * and will be just handed out, without caring for its lifecycle. - * If client code accesses this function via the plain C interface, - * the client is responsible for terminating this process, whereas - * when using the C++ interface, you'll get a Handle object which - * manages the lifecycle automatically. - */ - ProcessImpl* - DummyPlayerService::start (LumieraDisplaySlot viewerHandle) - { - auto_ptr newProcess (new ProcessImpl (viewerHandle)); - - REQUIRE (!newProcess->isActive()); - newProcess->setRate(25); - - return newProcess.release(); - } - - - - - - - /* === Process Implementation === */ - - - ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) - : fps_(0) - , play_(false) - , display_(Display::facade().getHandle (viewerHandle)) - , imageGen_(0) - , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) - { } - - - ProcessImpl::~ProcessImpl() - { - INFO (proc_dbg, "Playback process halted..."); - } - - - void - ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle - { - if (process) - delete process; - } - - - - DummyPlayer::Process - ProcessImpl::createHandle() - { - DummyPlayer::Process handle; - handle.activate(this, &terminate); // note the deleter function... - return handle; - } - - - - void - ProcessImpl::setRate (uint fps) - { - REQUIRE (fps==0 || fps_==0 ); - REQUIRE (fps==0 || !play_ ); - REQUIRE (tick_); - - fps_ = fps; - play_ = (fps != 0); - - if (play_) - imageGen_.reset(new DummyImageGenerator(fps)); - - // callbacks with given frequency, starting now - tick_->activate(fps); - } - - - - void - ProcessImpl::doPlay(bool yes) - { - REQUIRE (isActive()); - tick_->activate (yes? fps_:0); - play_ = yes; - } - - - - void - ProcessImpl::doFrame() - { - REQUIRE (isActive()); - ASSERT (imageGen_); - - if (play_) - display_(imageGen_->next()); - else - display_(imageGen_->current()); - } + /** */ - - } // namespace play - -} // namespace proc - - - - - -namespace lumiera { /* === Forwarding function(s) on the Process handle === */ - - void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } - - - - - - /** @internal intended for use by main(). */ - lumiera::Subsys& - DummyPlayer::getDescriptor() - { - return proc::play::theDummyPlayerDescriptor(); - } - - // emit the vtable here into this translation unit within liblumieraproc.so ... - DummyPlayer::~DummyPlayer() { } - - -} // namespace lumiera +}} // namespace proc::play diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index 75e0f3d78..ee9a04188 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -1,8 +1,8 @@ /* - DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + PLAY-SERVICE.hpp - interface: render- and playback control Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -20,35 +20,23 @@ */ -/** @file dummy-player-service.hpp - ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. - ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The - ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding - ** the necessary handles and allocations and providing an uniform API to the client side. - ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and - ** it uses an output handle (functor) to push the generated frames up. - ** - ** This service is the implementation of a layer separation facade interface. Clients should use - ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used - ** to \em provide this service, not to access it. +/** @file play-service.hpp + ** Player subsystem...... ** ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example */ -#ifndef PROC_DUMMYPLAYER_SERVICE_H -#define PROC_DUMMYPLAYER_SERVICE_H +#ifndef PROC_PLAY_PLAY_SERVICE_H +#define PROC_PLAY_PLAY_SERVICE_H -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" +#include "lib/error.hpp" #include -#include -#include +//#include +//#include namespace proc { @@ -60,95 +48,22 @@ namespace proc { using lumiera::DummyPlayer; - class DummyImageGenerator; - class TickService; - - - /******************************************************************** - * Actual implementation of a single (dummy) playback process. - * The DummyPlayerService (see below) maintains a collection of such - * actively running playback processes, while the client code gets - * DummyPlayer::Process handles to track any ongoing use. Users of - * the plain C interface get a direct bare pointer to the respective - * ProcessImpl instance and have to manage the lifecycle manually. - */ - class ProcessImpl - : public lumiera_playprocess, - boost::noncopyable - { - uint fps_; - bool play_; - - Display::Sink display_; - boost::scoped_ptr imageGen_; - boost::scoped_ptr tick_; - - - public: - ProcessImpl(LumieraDisplaySlot) ; - ~ProcessImpl() ; - - - /* Implementation-level API */ - - /** activate a playback process - * with given specification */ - void setRate (uint fps); - - bool isActive () { return fps_ != 0; } - bool isPlaying() { return play_; } - - void doPlay(bool yes); - - - /* Lifecycle */ - - DummyPlayer::Process createHandle(); - static void terminate(ProcessImpl* process); - - private: - void doFrame (); ///< periodically invoked while playing - }; +// class DummyImageGenerator; +// class TickService; /****************************************************** - * Actual implementation of the DummyPlayer service. - * Creating an instance of this class automatically - * registers the interface lumieraorg_DummyPlayer with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. + * Interface: Player subsystem. */ - class DummyPlayerService + class PlayService : boost::noncopyable { - string error_; - Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - public: - DummyPlayerService(Subsys::SigTerm terminationHandle); + PlayService(Subsys::SigTerm terminationHandle); - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); + ~PlayService() { notifyTermination_(&error_); } }; diff --git a/src/proc/play/sound/jack-output.cpp b/src/proc/play/sound/jack-output.cpp index 96600bf3b..4fc576d0d 100644 --- a/src/proc/play/sound/jack-output.cpp +++ b/src/proc/play/sound/jack-output.cpp @@ -1,8 +1,8 @@ /* - DisplayService - service providing access to a display for outputting frames + JackOutput - Jack audio connection client Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -21,232 +21,11 @@ * *****************************************************/ -#include "gui/display-service.hpp" - -extern "C" { -#include "common/interfacedescriptor.h" -} - - -namespace gui { +#include "proc/play/sound/jack-output.hpp" + +namespace proc { +namespace play { +namespace sound { - - namespace { // hidden local details of the service implementation.... - - - - /* ================== define an lumieraorg_Display instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DisplayFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\323\343\324\023\064\216\120\201\073\056\366\020\110\263\060\023", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Display"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\305\026\070\133\033\357\014\202\203\270\174\072\341\256\226\235", - const char*, (LumieraInterface ifa), - { (void)ifa; return "UI Interface: service for outputting frames to a viewer or display"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\170\104\246\175\123\144\332\312\315\263\071\170\164\213\024\275", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\265\343\045\346\110\241\276\111\217\120\155\246\230\341\344\124", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\302\027\122\045\301\166\046\236\257\253\144\035\105\166\070\103", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\074\013\020\161\075\135\302\265\260\000\301\147\116\355\035\261", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\037\232\153\100\114\103\074\342\164\132\370\210\372\164\115\275", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\026\243\334\056\125\245\315\311\155\375\262\344\007\076\341\254", - const char*, (LumieraInterface ifa), - { - (void)ifa; - 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_INTERFACE_INLINE (state, "\243\302\332\160\060\272\155\334\212\256\303\141\160\063\164\154", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\363\125\123\060\231\147\053\017\131\341\105\157\231\273\334\136", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DisplayService implementation... - - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_Display, 0 - ,lumieraorg_DisplayService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DisplayFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (allocate, "\177\221\146\253\255\161\160\137\015\005\263\362\307\022\243\365", - void, (LumieraDisplaySlot slotHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (slotHandle); - try - { - _instance->allocate (slotHandle,true); - } - catch (lumiera::Error&){ /* error state remains set */ } - } - ) - , LUMIERA_INTERFACE_INLINE (release, "\166\374\106\313\011\142\115\161\111\110\376\016\346\115\240\364", - void, (LumieraDisplaySlot slotHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (slotHandle); - _instance->allocate (slotHandle,false); - } - ) - , LUMIERA_INTERFACE_INLINE (put, "\340\062\234\227\152\131\370\272\146\207\224\015\361\070\252\135", - void, (LumieraDisplaySlot slotHandle, LumieraDisplayFrame frame), - { - //skipping full checks for performance reasons - REQUIRE (_instance && !lumiera_error_peek()); - - REQUIRE (slotHandle); - DisplayerSlot& slot = _instance->resolve (slotHandle); - slot.put (frame); - } - ) - ); - - - - - } // (End) hidden service impl details - - - - - DisplayService::DisplayService() - : error_("") - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_Display, 0, lumieraorg_DisplayService)) - { - INFO (progress, "Display Facade opened."); - } - - - - LumieraDisplaySlot - DisplayService::setUp (FrameDestination const& outputDestination) - { - DisplayerTab& slots (_instance->slots_); - return &slots.manage (new DisplayerSlot (outputDestination)); - } - - - - void - DisplayService::allocate (LumieraDisplaySlot handle, bool doAllocate) - { - REQUIRE (handle); - if (doAllocate) - { - if (handle->put_) - throw lumiera::error::Logic("slot already allocated for output"); - else - // Mark the handle as "allocated" and ready for output: - // Place the function pointer from the C interface into the handle struct. - // calling it will invoke the implementing instance's "put" function - // (see the LUMIERA_INTERFACE_INLINE above in this file!) - handle->put_ = serviceInstance_.get().put; - } - else - handle->put_ = 0; - } - - - - DisplayerSlot& - DisplayService::resolve (LumieraDisplaySlot handle) - { - REQUIRE (handle); - REQUIRE (handle->put_, "accessing a DisplayerSlot, which hasn't been locked for output"); - - return *static_cast (handle); - } - - - - - - /* === DisplayerSlot Implementation === */ - - - DisplayerSlot::DisplayerSlot (FrameDestination const& outputDestination) - : currBuffer_(0) - { - put_ = 0; // mark as not allocated - hasFrame_.connect (outputDestination); - dispatcher_.connect (sigc::mem_fun (this, &DisplayerSlot::displayCurrentFrame)); - } - - - DisplayerSlot::~DisplayerSlot() - { - TRACE (gui_dbg, "Displayer Slot closing..."); - } - - - void - DisplayerSlot::displayCurrentFrame() - { - hasFrame_.emit (currBuffer_); - } - - -} // namespace proc +}}} // namespace proc::play::sound diff --git a/src/proc/play/sound/jack-output.hpp b/src/proc/play/sound/jack-output.hpp index 1dec2c192..d793662d3 100644 --- a/src/proc/play/sound/jack-output.hpp +++ b/src/proc/play/sound/jack-output.hpp @@ -1,8 +1,8 @@ /* - DISPLAY-SERVICE.hpp - service providing access to a display for outputting frames + JACK-OUTPUT.hpp - Jack audio connection client Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, 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 @@ -20,21 +20,7 @@ */ -/** @file display-service.hpp - ** A public service provided by the GUI, implementing the lumiera::Display facade interface. - ** It serves two purposes: - ** - It maintains a collection of DisplayerSlot objects, which are the actual connection points - ** and allow to receive frames and dispatch them to the GTK main event loop thread. - ** Conceptually, creating such a slot means providing a possible display for output. - ** - It provides the actual implementation of the Display facade interface, i.e. the function - ** which is to invoked periodically by the playback processes to dispose a new frame into - ** the display. - ** - ** This service is the implementation of a layer separation facade interface. This header defines - ** the interface used to \em provide this service, not to access it. Clients get a specific - ** LumieraDisplaySlot passed as parameter when initiating a playback process from the GUI. Using - ** this LumieraDisplaySlot handle, clients should then use lumiera::DummyPlayer#facade to access - ** an implementation instance of this service in order to push actual frames up. +/** @file jack-output.hpp ** ** @see lumiera::Display ** @see lumiera::DummyPlayer @@ -42,149 +28,26 @@ */ -#ifndef GUI_DISPLAY_SERVICE_H -#define GUI_DISPLAY_SERVICE_H +#ifndef PROC_PLAY_SOUND_JACK_OUTPUT_H +#define PROC_PLAY_SOUND_JACK_OUTPUT_H -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" -#include "lib/scoped-ptrvect.hpp" - -#include -#include -#include -#include -#include +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +//#include "lib/scoped-ptrvect.hpp" +// +//#include +//#include +//#include +//#include +//#include -namespace gui { - - using std::string; - using std::vector; - using lumiera::Display; - using Glib::Dispatcher; +namespace proc { +namespace play { +namespace sound { - typedef sigc::slot FrameDestination; - typedef sigc::signal FrameSignal; - - - - /******************************************************************** - * Actual implementation of a single displayer slot. Internally, - * it is connected via the Glib::Dispatcher for outputting frames - * to a viewer widget, which executes within the GTK event thread. - * @note must be created from the GTK event thread. - */ - class DisplayerSlot - : public lumiera_displaySlot, - boost::noncopyable - { - Dispatcher dispatcher_; - FrameSignal hasFrame_; - - LumieraDisplayFrame currBuffer_; - - - public: - DisplayerSlot (FrameDestination const&) ; - ~DisplayerSlot () ; - - /* Implementation-level API to be used by DisplayService */ - - /** receive a frame to be displayed */ - inline void put (LumieraDisplayFrame); - - - private: - /** internal: activated via Dispatcher - * and running in GTK main thread */ - void displayCurrentFrame(); - - }; - - typedef lib::ScopedPtrVect DisplayerTab; - - - - /****************************************************** - * Actual implementation of the DisplayService. - * Creating an instance of this class automatically - * registers the interface lumieraorg_Display with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - * \par - * In addition to the Display interface, this class - * implements an additional service for the GUI, - * allowing actually to set up display slots, which - * then can be handed out to client code in the - * course of the play process for outputting frames. - */ - class DisplayService - : boost::noncopyable - { - - string error_; - DisplayerTab slots_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_Display, 0) - , lumiera::Display - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - - - public: - DisplayService(); - ~DisplayService() { - INFO (proc_dbg, "Display service dying..."); - - } - - - /** open a new display, sending frames to the given output destination - * @return handle for this slot, can be used to start a play process. - * NULL handle in case of any error. */ - static LumieraDisplaySlot setUp (FrameDestination const&); - - - /** prepare and the given slot for output - * @param doAllocate allocate when true, else release it - * @throw lumiera::error::Logic when already in use */ - void allocate (LumieraDisplaySlot, bool doAllocate); - - - /** resolve the given display slot handle to yield a ref - * to an actual implementation object. In order to be resolvable, - * the DisplayerSlot needs to be locked (=allocated) for output use. */ - DisplayerSlot& resolve (LumieraDisplaySlot); - - }; - - - - - void - DisplayerSlot::put(LumieraDisplayFrame newFrame) - { - if (newFrame != currBuffer_) - { - currBuffer_ = newFrame; - dispatcher_.emit(); - } - else - { - TRACE (render, "frame dropped?"); - } - } - - - -} // namespace gui +}}} // namespace proc::play::sound #endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 4b7b80c02..d9d884eb8 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1632,25 +1632,35 @@ The main tool used to implement this separation is the [[Builder Pattern|http:// Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
-
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
 
 !Starting point
-The idea is to start out with the design of the PlayerDummy and to //transform//&nbsp; it into the full player subsystem.
+The intention is to start out with the design of the PlayerDummy and to //transform//&nbsp; it into the full player subsystem.
 * the ~DisplayService in that dummy player design moves down into Proc-Layer and becomes the OutputManager
 * likewise, the ~DisplayerSlot is transformed into the interface OutputSlot, with various implementations to be registered with the OutputManager
 * the core idea of having a play controler act as the frontend and handle to an PlayProcess is retained.
 
+!!Idea
+Introduce a new kind of MObject, a GeneratorMO, to represent what the current dummy player does: generate some kind of test data. As a first step, we might retro-fit the existing dummy player to this structure, and then expand it to have a minimal render engine setup for tests and diagnostics. That is, there will be some kind of GeneratorNode to produce test data.
+
 !!Stages of transition
-Reworking the dummy player into the PlayerSubsystem is a larger undertaking, and best broken up into several //stages.//
+Reworking the dummy player into the [[Player subsystem|PlayService]] is a larger undertaking, and best broken up into several //stages.//
 # just moving the existing entities into the final location (typically down in the layer hierarchy)
 # introduce a GeneratorMO and a GeneratorNode, while providing a shortcut to get the GeneratorNode without requiring the (not yet implemented) Builder.
 # rework the ~ProcessImpl and the ~TickService to become the CalculationStream and use the real Engine Interface, but still with an mock implementation hooked up behind
 # extend and elaborate the handling of output, build the foundation of the real OutputManager
 # switch to the real Scheduler and Engine implementation
 
-!!Idea
-Introduce a new kind of MObject, a GeneratorMO, to represent what the current dummy player does: generate some kind of test data. As a first step, we might retro-fit the existing dummy player to this structure, and then expand it to have a minimal render engine setup for tests and diagnostics. That is, there will be some kind of GeneratorNode to produce test data.
+!!!!new entities to build {{red{#805}}} +* the ~DisplayService needs to be generalised into an OutputManager interface +* we need to introduce a new kind of StructAsset, the ViewerAsset. +* then, of course, the PlayService interface needs to be created +* as a temporary soltion, a ''~DummyPlayConnection'' will be created hard-wired, within the ~DummyPlayer service. This dumy container is a test setup, explicitly creating a OutputManager implementation, a GeneratorMO, a GeneratorNode (thus omitting the not-yet-implemented Builder) and the corresponding ModelPort. This way, the ~DummyPlayer service can be changed to use the real PlayService for creating the dummy generated output data. It will then pass back the resulting PlayController to the existing GUI setup. + +The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach tries to circumvent the problems with occasional crashes on shutdown, due to dispatching frame output signals in the event thread while already in shutdown. + +
LayerSeparationInterface provided by the GUI.
@@ -1713,6 +1723,15 @@ When building the low-level model, the actual processing code is resolved and a
 Initially, only the parameter (descriptors) are present on the effect ~MObject, while the actual [[parameter providers|ParamProvider]] are created or wired (by the ConManager) on demand.
 
+
+
The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|CalculationStream]].
+
+Below this facade, there is a thin adaptadion and forwarding layer, mainly talking to
+* the individual CalculationStream elements created for each PlayProcess.
+* the FrameDispatcher, which translates such streams into a series of RenderJob entries
+* the [[Scheduler]], which is responsible to perform these jobs in a timely fashion
+
+
A general identification scheme, ombining a human readable symbolic name, unique within a //specifically typed context,// and machine readable hash ID (LUID). ~Entry-IDs allow for asset-like position accounting and for type safe binding between configuration rules and model obects. They allow for creating an entry with symbolic id and distinct type, combined with an derived hash value, without the overhead in storage and instance management imposed by using a full-fledged Asset.
                                                                                       
@@ -1854,19 +1873,19 @@ The fixture is like a grid, where one dimension is given by the [[model ports|Mo
 :Thus the exit nodes are keyed by ~Pipe-ID as well (and consequently have a distinct [[stream type|StreamType]]) -- each model port coresponds to {{{<number_of_segments>}}} separate exit nodes, but of course an exit node may be //mute.//
 
-
+
Generally speaking, the [[here|Fixture]] comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline.
-This page focusses on the actual datastructure and usage details on that level. See also &rarr; [[storage|FixtureStorage]] considerations.
+This page focusses on the actual data structure and usage details on that level. See also &rarr; [[storage|FixtureStorage]] considerations.
 
 !transactional switch
-A key point to note is the fact that the fixture is frequently [[re-built||BuildFixture]] by the [[Builder]], while render processes may be going in in parallel. Thus, when a build process is finished, a transactional ''commit'' happens to ''hot swap'' the new parts of the model. This is complemented by a cleanup of tainted render processes; finally, storage can be relaimed.
+A key point to note is the fact that the fixture is frequently [[re-built||BuildFixture]] by the [[Builder]], while render processes may be going on in parallel. Thus, when a build process is finished, a transactional ''commit'' happens to ''hot swap'' the new parts of the model. This is complemented by a clean-up of tainted render processes; finally, storage can be reclaimed.
 
 To support this usage pattern, the Fixture implementation makes use of the [[PImpl pattern|http://c2.com/cgi/wiki?PimplIdiom]]
 
 !Collecting usage scenarios {{red{WIP 12/10}}}
 * ModelPort access
 ** get the model port for a given ~Pipe-ID
-** ennumerate the model ports for a Timeline
+** enumerate the model ports for a Timeline
 * rendering frame dispatch
 ** get or create the frame dispatcher table
 ** dispatch a single frame to yield the corresponding ExitNode
@@ -1875,16 +1894,16 @@ To support this usage pattern, the Fixture implementation makes use of the [[PIm
 ** create a new segmentation
 ** establish what segments actually need to be rebuilt
 ** dispatch a newly built segment into the transaction
-** schedule superseded segments and tainted process for cleanup
+** schedule superseded segments and tainted process for clean-up
 ** commit a transaction
  
 
 !Conclusions about the structure {{red{WIP 12/10}}}
 * the ~PImpl needs to be a single (language) pointer. This necessitates having a monolithic Fixture implementation holder
-* moreover, this necessitates a tight integration up to implementation level, both with the cleanup and the render processes themselves
+* moreover, this necessitates a tight integration down to implementation level, both with the clean-up and the render processes themselves
 
-
+
The Fixture &rarr; [[data structure|FixtureDatastructure]] acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel).
 Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes are known to be terminated.
 
@@ -1922,7 +1941,7 @@ But the primary question here is to judge the impact of such an implementation.
 
 !!!identifying tainted and disposable segments
 Above estimation hints at the necessity of frequently finding some 30 to 100 segments to be disposed, out of thousands, assumed the original reason for triggering the build process was a typically local change in the high-level model. We can only discard when all related processes are finished, but there is a larger number of segments as candidate for eviction. These candidates are rather easy to pinpoint -- they will be uncovered during a linear comparison pass prior to commiting the changed Fixture. Usually, the number of candidates will be low (localised changes), but global manipulations might invalidate thousands of segments.
-* if we frequently pick the segments actually to disposed, there is the danger of performance degeneration when the number if segments is high
+* if we frequently pick the segments actually to be disposed, there is the danger of performance degeneration when the number of segments is high
 * the other question is if we can afford just to keep all of those candidates around, as all of them are bound to get discardable eventually
 * and of course there is also the question how to detect //when// they're due.
 
@@ -1935,7 +1954,7 @@ Above estimation hints at the necessity of frequently finding some 30 to 100 seg
 //currently {{red{12/10}}} I tend to prefer Model B...// while the priority queue remains to be investigated in more detail for organising the actual build process.
 But actually I'm struck here, because of the yet limited knowledge about those render processes....
 * how do we //join// an aborted/changed rendering process to his successor, without creating a jerk in the output?
-* is it even possible to continue a process when parts of the covered timerange is affected by a build?
+* is it even possible to continue a process when parts of the covered timerange are affected by a build?
 If the latter question is answered with "No!", then the problem gets simple in solution, but maybe memory consuming: In that case, //all//&nbsp; processes linked to a timeline gets affected and thus tainted; we'd just dump them onto a pile and delay releasing all of the superseeded segments until all of them are known to be terminated.
 
@@ -1976,6 +1995,9 @@ Additionally, they may be used for resource management purposes by embedding a r #* one OpenGL Dataframe could contain raw texture data (but I am lacking expertise for this topic)
+
+
An implementation facility within the RenderEngine, responsible for translating a logical CalculationStream (corresponding to a PlayProcess) into a sequence of individual RenderJob entries, which can then be handed over to the [[Scheduler]]. Performing this operation involves a special application of [[time quantisation|TimeQuant]]: after establishing a suitable starting point, a typically contiguous series of frame numbers need to be generated, together with the time coordinates for each of those frames
+
/***
 |Name|FullScreenPlugin|
@@ -2967,12 +2989,12 @@ These are used as token for dealing with other objects and have no identity of t
 
 
-
+
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denotes a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
 
-A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination and some other entity actually uses this designation as a target, either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
+A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination, while some other entity at the same time actually //uses this designation as a target,// either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
 
-Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
+Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewConnection]]. Any render or display process happens at a model port.
 
 !formal specification
 Model port is a //conceptual entity,// denoting the possibility to pull generated data of a distinct (stream)type from a specific bus within the model -- any possible output produced or provided by Lumiera is bound to appear at a model port. The namespace of model ports is global, each being associated with a ~Pipe-ID.
@@ -3224,7 +3246,7 @@ Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputMa
 &rarr; see also the PlayerDummy
 
-
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
 !Mapping situations
@@ -3235,8 +3257,8 @@ Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputMa
 :any Timeline produces a number of [[model ports|ModelPort]]. On the other hand, any viewer exposes a small number of output designations, representing the image and sound output(s).
 :Thus, in this case we resolve similar to a bus connection, possibly overridden by already pre-existing or predefined connections.
 ;switch board
-:a viewer might receive multiple outputs and overlays, necessitating a user operated control to select what's actually to displayed
-:Thus, in this case we need a backwards resolution at the lower end of the output network, to connect to the model port as defined by the switchboard
+:a viewer might receive multiple outputs and overlays, necessitating a user operated control to select what's actually to be displayed
+:Thus, in this case we need a backwards resolution at the lower end of the output network, to connect to the model port as defined by this viewer SwitchBoard
 ;global pipes or virtual media
 :when binding a Sequence as Timeline or VirtualClip, a mapping from output designations used within the Sequence to virtual channels or global pipes is required
 :Thus, in this case we need to associate output designations with ~Pipe-IDs encountered in the context according to some rules &mdash; again maybe overridden by pre-existing connections
@@ -4114,15 +4136,38 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T
 <<tasksum end>>
 
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
-The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls dispatched as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
+The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
-''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this stream of calculations happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
-* the [[Backend]] is responsible for scheduling and dispatching the calculations
+''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalculationStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
+* the [[Backend]] is responsible for scheduling and [[dispatching|Dispatcher]] the CalculationStream
 * the RenderEngine has the ability to cary out individual frame calculations
 * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
+
+
The [[Player]] is an independent [[Subsystem]] within Lumiera, located at Proc-Layer level. A more precise term would be "rendering and playback coordination subsystem". It provides the capability to generate media data, based on a high-level model object, and send this generated data to an OutputDesignation, creating an continuous and timing controlled output stream. Clients may utilise these functionality through the ''play service'' interface.
+
+!provided services
+* creating a PlayProcess
+* managing existing play processes
+* convenience short-cuts for //performing//&nbsp; several kinds of high-level model objects
+
+!creating a play process
+This is the core service provided by the player subsystem. The purpose is to create a collaboration between several entities, creating media output
+;data producers
+:a set of ModelPort elements to ''pull'' for generating output. They can either be handed in direcly, or resolved from a set of OutputDesignation elements
+;data sinks
+:to be able to create output, the PlayProcess needs to cooperate with OutputSlot.s
+:physical outputs are never handled directly, rather, the playback or rendering needs an OutputManager to resolve the output designations into output slots
+;controller
+:when provided with these two prerequisites, the play service is able to build a PlayProcess.
+:for clients, this process can be accessed and maintained through a PlayController, which acts as (copyable) handle and front-end.
+;engine
+:the actual processing is done by the RenderEngine, which in itself is a compound of several services within [[Backend]] and Proc-Layer
+:any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade
+
+
Within Lumiera, &raquo;Player&laquo; denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
 The player subsystem does not perform or even manage any render operations, nor does it handle the outputs directly.
@@ -4722,9 +4767,10 @@ The link between ~MObject and Asset should be {{{const}}}, so the clip can't cha
 At first sight the link between asset and clip-MO is a simple logical relation between entities, but it is not strictly 1:1 because typical media are [[multichannel|MultichannelMedia]]. Even if the media is compound, there is //only one asset::Clip//, because in the logical view we have only one "clip-thing". On the other hand, in the session, we have a compound clip ~MObject comprised of several elementary clip objects, each of which will refer to its own sub-media (channel) within the compound media (and don't forget, this structure can be tree-like)
 {{red{open question:}}} do the clip-MO's of the individual channels refer directly to asset::Media? does this mean the relation is different from the top level, where we have a relation to a asset::Clip??
-
+
Conceptually, the Render Engine is the core of the application. But &mdash; surprisingly &mdash; we don't even have a distinct »~RenderEngine« component in our design. Rather, the engine is formed by the cooperation of several components spread over two layers (Backend and Proc-Layer): The [[Builder]] creates a network of [[render nodes|ProcNode]], which is used by the Backend to pull individual Frames.
 &rarr; OverviewRenderEngine
+&rarr; EngineFaçade 
 
@@ -4910,7 +4956,7 @@ We need to detect attaching and detaching of * root &harr; [[Track]]
-
+
//Segmentation of timeline// denotes a data structure and a step in the BuildProcess.
 When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Placements -- are grouped below each timeline using them; Placements are then to be resolved into [[explicit Placements|ExplicitPlacement]], resulting in a single well defined time interval for each object. This allows to cut this effective timeline into slices of constant wiring structure, which are represented through the ''Segmentation Datastructure'', a time axis with segments holding object placements and [[exit nodes|ExitNode]]. &nbsp;&rarr; see [[structure of the Fixture|Fixture]]
 * for each Timeline we get a Segmentation
@@ -4940,7 +4986,7 @@ When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Pla
 
 !!!conclusions
 The Fixture is mostly comprised of the Segementation datastructure, but some other facilities are involved too
-# at top level, access is structured by groups of model ports, actually grouped by timeline. This first access level handled by the Fixture
+# at top level, access is structured by groups of model ports, actually grouped by timeline. This first access level is handled by the Fixture
 # during the build process, there is a collection of placements; this can be discarded afterwards
 # the backbone of the segmentation is closely linked to an ordering by time. Initially it should support sorting, access by time interval search later on.
 # discarding a segment (or failing to do so) has an high impact on the whole application. We should employ a reliable mechanism for that.
@@ -5725,6 +5771,14 @@ h1,h2,h3,h4,h5,h6 {
 /*}}}*/
 
+
+
Viewers for displaying output are connected to the timelines or other elements to be displayed through a ViewConnection. This creates an additional mapping step (&rarr; BindingMO), similar to the attachment of a sequence or virtual clip to the [[global busses|GlobalBus]]. Yet the viewers don't provide such an elaborate mixing desk like view as the timelines -- rather there is only a simplified version of the global busses, exposed to the user as ''switch board''.
+
+The corresponding GUI control will allow to choose among possible data sources (esp. ProbePoint) for any given StreamType (more precisely, for every //prototype,// e.g. image, sound). Moreover, there are some (limited) overlay capabilities (split image, mix sound). By default, this additional control will likely be hidden, as there is a default 1:1 association between the master busses of the timeline and the usable output destinations.
+
+!Model and Interfaces
+Regarding the internal wiring of components, the Viewer with a switch board behaves similar as a timeline: At the output side, there are a small number of standard OutputDesignation elements for the selection of primary kinds of media handled within this session (typically Video and Audio). But contrary to a timeline, a Viewer element also exposes an OutputManager interface, which is backed by a specific OutputMapping element, which internally connects to the main OutputManager for the whole Application. This way, a Viewer has the capability to get a PlayController
+
<<timeline better:true maxDays:55 maxEntries:45>>
@@ -6874,7 +6928,7 @@ At the level of individual timecode formats, we're lacking a common denominator; &rarr; Quantiser [[implementation details|QuantiserImpl]]
-
+
the following collection of usage situations helps to shape the details of the time values and time quantisation design. &rarr; see also  [[time quantisation|TimeQuant]]
 
 ;time position of an object
@@ -6894,6 +6948,8 @@ At the level of individual timecode formats, we're lacking a common denominator;
 :because this operation might be done both in the quantised or non-quantised domain, and also the result might be (un)quantised
 ;updating the playback position
 :this can be seen as a practical application of the above; basically we can choose to show the wall clock time or we can advance the playback position in frame increments, thus denoting the frame currently in display. For video, these distinctions may look moot, but they are indeed relevant for precise audio editing, especially when combined with loop playback (recall that audio is processed block wise, but the individual sample frames and thus the possible loop positions are way finer than the processing block size)
+;dispatching individual frames for calculation
+:when a [[render or playback process|PlayProcess]] is created, at some point we need to translate this logical unit ("calculation stream") into individual frame job entries. This requires to break continuous time into individual frames, and then ennumerating these frames.
 ;displaying time intervals
 :for display, time intervals get //re-quantised// into display array coordinates.
 :While evidently the display coordinates are themselves quantised and we obviously don't want to cancel out the effect of an quantisation of the values or intervals to be displayed (which means, we get two quantisations chained up after each other), there remains the question if the display array coordinates should be aligned to the grid of the //elements to be displayed,// and especially if the allowed zoom factors should be limited. This decision isn't an easy one, as it has an immediate and tangible effect on what can be showed, how reversible and reproducible a view is and (especially note this!) on the actual values which can be set and changed through the GUI.
@@ -6915,6 +6971,8 @@ Another closely related problem is ''when to allow [[mutations|TimeMutation]]'',
 
 The ''problem with playback position'' is -- that indeed it's an attempt to conceptualise a non-existing entity. There is no such thing like "the" playback position. Yet most applications I'm aware off //do// employ this concept. Likely they got trapped by the metaphor of the tape head, again. We should do away with that. On playback, we should show a //projection of wall-clock time onto the expected playback range// -- not more, not less. It should be acknowledged that there is //no direct link to the ongoing playback processes,// besides the fact that they're assumed to sync to wall-clock time as well. Recall, typically there are multiple playback processes going on in compound, and each might run on a different update rate. If we really want a //visual out-of-sync indicator,// we should treat that as a separate reporting facility and display it apart of the playback cursor.
 
+An interesting point to note for the ''frame dispatch step'' is the fact, that in this case quantised values and quantisation are approached in the reverse direction, compared with the other uses. Here, after establishing a start point on the time scale, we proceed with ennumerating distinct frames and later on need to access the corresponding raw time, especially to find out about the [[timeline segment|Segmentation]] to address, or for retrieving parameter automation. &rarr; FrameDispatcher.
+
 Note that the ''display window might be treated as just an independent instance of quantisation''. This is similar to the approach taken above for modifying quantised time span values. We should provide a special kind of time grid, the display coordinates. The origin of these is always defined to the left (lower) side of the interval to be displayed, and they are gauged in screen units (pixels or similar, as used by the GUI toolkit set). The rest is handled by the general quantisation mechanisms. The problem of aligning the display should be transformed into a general facility to align grids, and solved for the general case. Doing so solves the remaining problems with quantised value changes and with ''specifying relative placements'' as well: If we choose to represent them as quantised values, we might (or might not) also choose to apply this //grid-alignment function.//
 
 !the complete time value usage cycle
@@ -6976,12 +7034,13 @@ Currently (1/11), the strategy is implemented according to (1) and (4) above, le
 Implementation of this strategy is still broken: it doesn't work properly when actually the change passing over the zero point happens by propagation from lower digits. Because then -- given the way the mutators are implemented -- the //new value of the wrapping digit hasn't been stored.// It seems the only sensible solution is to change the definition of the functors, so that any value will be changed by side-effect {{red{Question 4/11 -- isn't this done and fixed by now??}}}
 
-
+
Timeline is the top level element within the [[Session (Project)|Session]]. It is visible within a //timeline view// in the GUI and represents the effective (resulting) arrangement of media objects, to be rendered for output or viewed in a Monitor (viewer window). A timeline is comprised of:
 * a time axis in abolute time ({{red{WIP 1/10}}}: not clear if this is an entity or just a conceptual definition) 
-* a PlayControler ({{red{WIP Summer 2010: see discussion on ML. It seems rather that the controller will be attached}}})
 * a list of [[global Pipes|GlobalPipe]] representing the possible outputs (master busses)
 * //exactly one// top-level [[Sequence]], which in turn may contain further nested Sequences.
+* when used for Playback, a ViewConnection is necessary, allowing to get or connect to a PlayController
+
 Please note especially that following this design //a timeline doesn't define tracks.// [[Tracks form a Tree|Track]] and are part of the individual sequences, together with the media objects placed to these tracks. Thus sequences are independent entities which may exist stand-alone within the model, while a timeline is //always bound to hold a sequence.// &rarr; see ModelDependencies
 [>img[Fundamental object relations used in the session|uml/fig136453.png]]
 
@@ -7202,14 +7261,29 @@ For now, as of 6/10, we use specialised QueryResolver instances explicitly and d
 &rarr; QueryRegistration
 
-
+
+
For any kind of playback to happen, timeline elements (or similar model objects) need to be attached to a Viewer element through a special kind of [[binding|BindingMO]], called a ''view connection''. In the most general case, this creates an additional OutputMapping (and in the typical standard case, this boils down to a 1:1 association, sending the master bus of each media kind to the standard OutputDesignation for that kind).
+
+Establishing a ~ViewConnection is prerequisite for creating or attaching an PlayController through the PlayService. Multiple "play control" GUI elements can be associated with such a play controller, causing them to work as being linked together: if you e.g. push "play" on one of them, the button states of all linked GUI controls will reflect the state change of the underlying play controller.
+
+View connections are part of the model and thus persistent. They can be created explicitly, or just derived by //allocating a viewer.// And a new view connection can push aside (and thus "break") an existing one from another timeline or model element. When a view connection is //broken,// any associated PlayProcess needs to be terminated (this is a blocking operation). Thus, at any time, there can be only one active view connection to a given viewer; here "active" means, that a PlayController has been hooked up, and the connection is ready for playback or rendering. But on the other hand, nothing prevents a timeline (or similar model object) to maintain multiple view connections -- consequently the actual playback position behaves as if associated with the view connection.
+
+
+
+
A [[structural Asset|StructAsset]] corresponding to a Viewer element in the GUI.
+
+These Viewer (or Monitor) elements play an enabling role for any output generation. In order to [[initiate playback|PlayService]], we need a fulle resolved OutputDesignation, which typcially can be achieved by creating a ViewConnection, i.e. connecting a timeline or similarily suitable model element to such a viewer. Actually, this connection is modelled by attaching to a BindingMO which is linked to a ViewerAsset. This way, the model tracks and persists the available viewer windows and the current connection situation.
+
+When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.
+
+
for showing output, three entities are involved
 * the [[Timeline]] holds the relevant part of the model, which gets rendered for playback
 * by connecting to a viewer component, an actual output target is established
 * the playback process itself is coordinated by a PlayController, which in turn creates a PlayProcess
 
 !the viewer connection
-A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputManager slot &mdash; again an output mapping step.
+A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputManager slot &mdash; again an output mapping step.
 
From 92301a3752bac4184105dab35a01c274c8acc2a1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 24 Jul 2011 02:51:41 +0200 Subject: [PATCH 011/296] SCons: adjust configure check for boost-filesystem to work for >= 1.42 NOTE: this breaks compatibility with Debian/Lenny --- SConstruct | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 250936bc7..318f3f53c 100644 --- a/SConstruct +++ b/SConstruct @@ -254,8 +254,10 @@ def configurePlatform(env): problems.append('We need boost::format (header).') if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): problems.append('We need boost::program_options (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'): + problems.append('We need the boost::system support library (including binary lib).') if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): - problems.append('We need the boost::filesystem (including binary lib for linking).') + problems.append('We need the boost::filesystem lib (including binary lib for linking).') if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): problems.append('We need the boost regular expression lib (incl. binary lib for linking).') From 20f95ca26f6c4f280379f046a27c395eeced5aad Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 24 May 2011 03:46:32 +0200 Subject: [PATCH 012/296] draft the player facade interface --- src/common/instancehandle.hpp | 8 +- src/common/interfaceproxy.cpp | 146 ++++++++++-------- src/include/display-facade.h | 19 ++- src/include/dummy-player-facade.h | 1 - src/include/interfaceproxy.hpp | 2 +- src/include/play-facade.h | 141 +++++++++++++++++ src/lib/p.hpp | 28 ++-- .../play/dummy-player-interface-proxy.hpp | 4 +- src/proc/play/output-manager.hpp | 8 +- 9 files changed, 260 insertions(+), 97 deletions(-) create mode 100644 src/include/play-facade.h diff --git a/src/common/instancehandle.hpp b/src/common/instancehandle.hpp index d6c98e546..9f8004c65 100644 --- a/src/common/instancehandle.hpp +++ b/src/common/instancehandle.hpp @@ -184,7 +184,7 @@ namespace lumiera { public: /** Set up an InstanceHandle representing a plugin. - * Should be placed at the client side. + * Should be placed at the client side. * @param iName unmangled name of the interface * @param version major version * @param minminor minimum acceptable minor version number @@ -203,7 +203,7 @@ namespace lumiera { * registration and deregistration of interface(s). * Should be placed at the service providing side. * @param a (single) interface descriptor, which can be created with - * LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF + * LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF */ InstanceHandle (LumieraInterface descriptor) : desc_(descriptor) @@ -222,9 +222,9 @@ namespace lumiera { - /** act as smart pointer providing access through the facade. + /** act as smart pointer providing access through the facade. * @note we don't provide operator* */ - FA * operator-> () const { return &(facadeLink_(*this)); } + FA * operator-> () const { return &(facadeLink_(*this)); } /** directly access the instance via the CL interface */ I& get () const { ENSURE(instance_); return *instance_; } diff --git a/src/common/interfaceproxy.cpp b/src/common/interfaceproxy.cpp index e403e40a5..611bdd97c 100644 --- a/src/common/interfaceproxy.cpp +++ b/src/common/interfaceproxy.cpp @@ -28,73 +28,87 @@ using util::cStr; -namespace lumiera { - namespace facade { - - - LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible"); - - - template - class Holder; - - template - class Holder > - : Accessor, - protected FA - { - protected: - typedef InstanceHandle IHandle; - typedef Holder THolder; - typedef Proxy TProxy; - typedef Accessor Access; - - I& _i_; - - Holder (IHandle const& iha) - : _i_(iha.get()) - { } - - public: - static TProxy& open(IHandle const& iha) - { - static char buff[sizeof(TProxy)]; - TProxy* p = new(buff) TProxy(iha); - Access::implProxy_ = p; - return *p; - } - - static void close() - { - if (!Access::implProxy_) return; - TProxy* p = static_cast (Access::implProxy_); - Access::implProxy_ = 0; - p->~TProxy(); - } - }; - - - template - FA* Accessor::implProxy_; - - - template - void - openProxy (IHA const& iha) - { - Proxy::open(iha); - } - - template - void - closeProxy () - { - Proxy::close(); - } - - } // namespace facade +namespace lumiera{ +namespace facade { -} // namespace lumiera + + LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible"); + + + /** + * Implementation Base + * for building Facade Proxy implementations. + * Typically the purpose of such a proxy is to route + * any calls through the C-Bindings of the Lumiera Interface system. + * The actual storage for the concrete proxy object is embedded, + * inline within the #open() function. For access by the clients, + * a frontend-object of type \c Accessor may be placed into + * the facade interface; this accessor-frontend is basically + * a concealed static pointer to the proxy, and will be set, + * when the interface is opened. This opening and closing + * of the interface itself is controlled by the + * InstanceHandle, which in turn is typically + * created and managed within the context + * of the service implementation. + */ + template + class Holder; + + template + class Holder > + : Accessor + , protected FA + { + protected: + typedef InstanceHandle IHandle; + typedef Holder THolder; + typedef Proxy TProxy; + typedef Accessor Access; + + I& _i_; + + Holder (IHandle const& iha) + : _i_(iha.get()) + { } + + public: + static TProxy& open(IHandle const& iha) + { + static char buff[sizeof(TProxy)]; + TProxy* p = new(buff) TProxy(iha); + Access::implProxy_ = p; + return *p; + } + + static void close() + { + if (!Access::implProxy_) return; + TProxy* p = static_cast (Access::implProxy_); + Access::implProxy_ = 0; + p->~TProxy(); + } + }; + + + template + FA* Accessor::implProxy_; + + + template + void + openProxy (IHA const& iha) + { + Proxy::open(iha); + } + + template + void + closeProxy () + { + Proxy::close(); + } + +}} // namespace lumiera::facade diff --git a/src/include/display-facade.h b/src/include/display-facade.h index d226fa6e5..66089287e 100644 --- a/src/include/display-facade.h +++ b/src/include/display-facade.h @@ -21,13 +21,18 @@ */ /** @file display-facade.h - ** Major public Interface of the Lumiera GUI. While, generally speaking, the GUI - ** controls the application and thus acts on its own, it exposes some services - ** to the lower layers. Especially the lumiera::Display interface serves to - ** hand over calculated frames to the GUI for displaying them in a viewer. - ** It's a first draft as of 1/2009, probably it can be factored out into - ** a more general display service in future. - ** + ** Experimental Interface, allowing the Dummy-Player to access the + ** video display widget in the GUI. While, generally speaking, the GUI + ** controls the application and thus acts on its own, it might expose some + ** services to the lower layers. + ** + ** In the Dummy-Player design study, the lumiera::Display interface serves + ** to hand over calculated frames to the GUI for displaying them in a viewer. + ** + ** This is a first draft as of 1/2009, and likely to be superseded by a + ** better design, where rather the \em provider of an output facility + ** registers with the OutputManager in the core. + ** ** @see gui::GuiFacade ** @see dummy-player-facade.h ** diff --git a/src/include/dummy-player-facade.h b/src/include/dummy-player-facade.h index 403ca2f35..af7ba31f7 100644 --- a/src/include/dummy-player-facade.h +++ b/src/include/dummy-player-facade.h @@ -35,7 +35,6 @@ #include "include/interfaceproxy.hpp" #include "lib/handle.hpp" -#include diff --git a/src/include/interfaceproxy.hpp b/src/include/interfaceproxy.hpp index ea5677042..a488ce65f 100644 --- a/src/include/interfaceproxy.hpp +++ b/src/include/interfaceproxy.hpp @@ -33,7 +33,7 @@ ** Typically there is another subclass of the Facade interfaces sitting "on the other side" ** of the interface barrier and actually implementing the functionality. The template ** facade::Accessor can be thought of as a factory creating such a proxy instance of the - ** facade interface for the client code to use. Typically, in instance of the \em factory + ** facade interface for the client code to use. Typically, an instance of the \em factory ** is embedded (as a static functor member object) right within the otherwise abstract ** facade interface, this way allowing the client code to write e.g. \c XYZInterface::facade() ** to yield a reference to a proxy object implementing \c XYZInterface. diff --git a/src/include/play-facade.h b/src/include/play-facade.h new file mode 100644 index 000000000..82af1fddb --- /dev/null +++ b/src/include/play-facade.h @@ -0,0 +1,141 @@ +/* + PLAYER-FACADE.h - access point to the Lumiera player subsystem + + Copyright (C) Lumiera.org + 2011, 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_PLAY_H +#define PROC_INTERFACE_PLAY_H + + + + +#ifdef __cplusplus /* ============== C++ Interface ================= */ + +//#include "include/interfaceproxy.hpp" +#include "lib/handle.hpp" +#include "lib/iter-source.hpp" +#include "lib/time/timevalue.hpp" +#include "proc/mobject/model-port.hpp" +#include "proc/mobject/output-designation.hpp" +#include "proc/mobject/session/clip.hpp" +#include "proc/mobject/session/track.hpp" +#include "proc/play/output-manager.hpp" +#include "asset/timeline.hpp" +#include "asset/viewer.hpp" + + + +namespace proc { + namespace play { + + class PlayProcess; +} } + + +namespace lumiera { + + + /****************************************************************** + * Interface to the Player subsystem of Lumiera (Proc-Layer). + * Global access point for starting playback and render processes, + * calculating media data by running the render engine. + * + * @todo WIP-WIP-WIP 6/2011 + * @note Lumiera is not yet able actually to deliver rendered data. + * @todo there should be an accompanying CL Interface defined for + * the Lumiera interface system, so the player can be + * accessed from external clients. This was left out + * for now, as we don't have either plugins, nor + * any script running capabilities yet. (5/2011) + */ + class Play + { + public: + + /** get an implementation instance of this service */ + static lumiera::facade::Accessor facade; + + + /** + * Continuous playback process, which has been hooked up + * and started with a fixed set of output slots. started with a specific + * output size, format and framerate. It is a handle to a calculation process, + * which is about to produce a stream of frames and push them to the outputs. + * + * The Lifecycle of the referred playback process is managed automatically + * through this handle (by ref count). Client code is supposed to use the + * API on this handle to navigate and control the playback mode. + * + * @see handle.hpp + * @see player-service.cpp implementation + */ + class Controller + : public lib::Handle + { + public: + void play(bool); ///< play/pause toggle + void adjustSpeed(double); ///< playback speed control + void go(lib::time::Time); + /////////////////////////////TODO how to modify the Loop range, the playback mode, scrubbing? + }; + + + typedef lib::IterSource ModelPorts; + typedef lib::IterSource Pipes; + typedef proc::play::POutputManager Output; + typedef mobject::session::PClipMO Clip; + typedef mobject::PTrack Track; + typedef asset::PTimeline Timeline; + typedef asset::PViewer Viewer; + + /** create a new playback process outputting to the given viewer/display */ + virtual Controller connect(ModelPorts, Output) =0; + + + /* ==== convenience shortcuts for common use cases ==== */ + Controller perform(Pipes, Output); + Controller perform(Timeline); + Controller perform(Viewer); + Controller perform(Track); + Controller perform(Clip); + + protected: + virtual ~Player(); + }; + + +} // namespace lumiera + + + + +extern "C" { +#endif /* =========================== CL Interface ===================== */ + + +// #include "common/interface.h" +////////////////////////////////////TODO define a C binding for the Interface system here + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/lib/p.hpp b/src/lib/p.hpp index 4fef50a36..cd7ccd776 100644 --- a/src/lib/p.hpp +++ b/src/lib/p.hpp @@ -79,51 +79,51 @@ namespace lumiera { P ( ) : BASE() {} template explicit P (Y* p) : BASE(p) {} template P (Y* p, D d) : BASE(p,d){} - + P (P const& r) : BASE(r) {} template P (shared_ptr const& r) : BASE(r) {} template explicit P (weak_ptr const& wr) : BASE(wr) {} - template explicit P (std::auto_ptr & ar) : BASE(ar) {} - + template explicit P (std::auto_ptr & ar) : BASE(ar) {} - P& operator= (P const& r) { BASE::operator= (r); return *this; } + + P& operator= (P const& r) { BASE::operator= (r); return *this; } template P& operator=(shared_ptr const& sr) { BASE::operator= (sr); return *this; } template P& operator=(std::auto_ptr & ar) { BASE::operator= (ar); return *this; } - + TAR* get() const { return dynamic_cast (BASE::get()); } TAR& operator*() const { return *get(); } TAR* operator->() const { return get(); } - + void swap(P& b) { BASE::swap (b);} - + private: /* === friend operators injected into enclosing namespace for ADL === */ template friend inline bool operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); } - + template friend inline bool operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); } - + template friend inline bool operator< (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p < *q; } ///< @note deliberately not allowing comparison on NIL ////TICKET #307 : problem with equality test in associative containers, where equal(a,b) := !(a < b) && !(b < a) - + template friend inline bool operator> (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *q < *p; } - + template friend inline bool operator<= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p <= *q;} - + template friend inline bool operator>= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p >= *q;} }; - - + + } // namespace lumiera #endif diff --git a/src/proc/play/dummy-player-interface-proxy.hpp b/src/proc/play/dummy-player-interface-proxy.hpp index 3e4ccdcd4..4753250a2 100644 --- a/src/proc/play/dummy-player-interface-proxy.hpp +++ b/src/proc/play/dummy-player-interface-proxy.hpp @@ -53,8 +53,8 @@ namespace lumiera { namespace facade { typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , proc::play::DummyPlayer - > IHandle_DummyPlayer; + , proc::play::DummyPlayer + > IHandle_DummyPlayer; template<> diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index b697f35d6..df3b4e216 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -36,12 +36,15 @@ #include //#include //#include +#include +namespace proc { namespace play { //using std::string; //using std::vector; + using std::tr1::shared_ptr; @@ -77,8 +80,9 @@ namespace play { public: OutputManager() {} }; - + + typedef shared_ptr POutputManager; -} // namespace play +}} // namespace proc::play #endif From c091b8e2603121038a799c35f0f81afedb307a81 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 24 May 2011 03:48:49 +0200 Subject: [PATCH 013/296] define Player / Output subsystem stub --- src/lumiera/main.cpp | 8 +++++-- src/proc/facade.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++ src/proc/facade.hpp | 9 ++++++++ wiki/renderengine.html | 4 ++-- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp index 14d039b7d..f738337bf 100644 --- a/src/lumiera/main.cpp +++ b/src/lumiera/main.cpp @@ -37,15 +37,15 @@ using lib::Cmdline; using lumiera::Subsys; using lumiera::AppState; -using lumiera::ON_GLOBAL_INIT; namespace { Subsys& engine = backend::EngineFacade::getDescriptor(); Subsys& netNode = backend::NetNodeFacade::getDescriptor(); Subsys& script = backend::ScriptRunnerFacade::getDescriptor(); - Subsys& player = lumiera::DummyPlayer::getDescriptor(); + Subsys& player = lumiera::DummyPlayer::getDescriptor(); ///////TODO: just a dummy, until we're able to render Subsys& builder = proc::Facade::getBuilderDescriptor(); Subsys& session = proc::Facade::getSessionDescriptor(); + Subsys& playOut = proc::Facade::getPlayOutDescriptor(); Subsys& lumigui = gui::GuiFacade::getDescriptor(); } @@ -66,13 +66,17 @@ main (int argc, const char* argv[]) session.depends (builder); netNode.depends (session); netNode.depends (engine); + playOut.depends (engine); + playOut.depends (session); // lumigui.depends (session); //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin // lumigui.depends (engine); + player.depends (playOut); //////TODO dummy player, until we're able to render lumigui.depends (player); script.depends (session); script.depends (engine); application.maybeStart (session); + application.maybeStart (playOut); application.maybeStart (netNode); application.maybeStart (lumigui); application.maybeStart (script); diff --git a/src/proc/facade.cpp b/src/proc/facade.cpp index 6b44fbd36..6ca6a84b6 100644 --- a/src/proc/facade.cpp +++ b/src/proc/facade.cpp @@ -103,9 +103,53 @@ namespace proc { } }; + + + class PlayOutSubsysDescriptor + : public Subsys + { + operator string () const { return "PlayOut"; } + + /** determine, if any output system is required to start up explicitly. + * Moreover, extract configuration variations for specific kinds of output + * @return true if any output system is required to start stand-alone. + * otherwise, the player and a default configured output connection + * is pulled up only when required by another subsystem (e.g. GUI) + * @todo actually define cmdline options and parse/decide here! + */ + bool + shouldStart (lumiera::Option&) + { + TODO ("extract options about specific output systems to be brought up"); + return false; + } + + bool + start (lumiera::Option&, Subsys::SigTerm termination) + { + UNIMPLEMENTED ("bring up and configure the output connections and the player"); + return false; + } + + void + triggerShutdown () throw() + { + UNIMPLEMENTED ("initiate playback/render halt and disconnect output"); + } + + bool + checkRunningState () throw() + { + //Lock guard (*this); + TODO ("implement detecting running state"); + return false; + } + }; + namespace { lib::Singleton theBuilderDescriptor; lib::Singleton theSessionDescriptor; + lib::Singleton thePlayOutDescriptor; } @@ -125,6 +169,14 @@ namespace proc { { return theSessionDescriptor(); } + + + /** @internal intended for use by main(). */ + Subsys& + Facade::getPlayOutDescriptor() + { + return thePlayOutDescriptor(); + } diff --git a/src/proc/facade.hpp b/src/proc/facade.hpp index 88ac62e56..e32227729 100644 --- a/src/proc/facade.hpp +++ b/src/proc/facade.hpp @@ -44,6 +44,8 @@ namespace proc { * @todo this is a dummy placeholder as of 1/2009. Currently, there * is only implementation-level code within the Proc-Layer and * the interfaces need to be worked out. + * @note at least the Play/Output subsystem slowly turns into + * something real, as of 6/2011 * */ struct Facade @@ -60,6 +62,13 @@ namespace proc { static lumiera::Subsys& getSessionDescriptor(); + /** provide a descriptor for lumiera::AppState, + * wired accordingly to allow main to bring up + * the render, playback coordination and + * output management subsystem. */ + static lumiera::Subsys& getPlayOutDescriptor(); + + //////////////////TODO: define the global access interfaces for the Proc-Layer //////////////////TODO: provide a function for accessing this interface //////////////////TODO: register similar proxy/facade interfaces for the GUI diff --git a/wiki/renderengine.html b/wiki/renderengine.html index d9d884eb8..b5da538ca 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -7269,10 +7269,10 @@ Establishing a ~ViewConnection is prerequisite for creating or attaching an Play View connections are part of the model and thus persistent. They can be created explicitly, or just derived by //allocating a viewer.// And a new view connection can push aside (and thus "break") an existing one from another timeline or model element. When a view connection is //broken,// any associated PlayProcess needs to be terminated (this is a blocking operation). Thus, at any time, there can be only one active view connection to a given viewer; here "active" means, that a PlayController has been hooked up, and the connection is ready for playback or rendering. But on the other hand, nothing prevents a timeline (or similar model object) to maintain multiple view connections -- consequently the actual playback position behaves as if associated with the view connection.
-
+
A [[structural Asset|StructAsset]] corresponding to a Viewer element in the GUI.
 
-These Viewer (or Monitor) elements play an enabling role for any output generation. In order to [[initiate playback|PlayService]], we need a fulle resolved OutputDesignation, which typcially can be achieved by creating a ViewConnection, i.e. connecting a timeline or similarily suitable model element to such a viewer. Actually, this connection is modelled by attaching to a BindingMO which is linked to a ViewerAsset. This way, the model tracks and persists the available viewer windows and the current connection situation.
+These Viewer (or Monitor) elements play an enabling role for any output generation. In order to [[initiate playback|PlayService]], we need a fully resolved OutputDesignation, which typcially can be achieved by creating a ViewConnection, i.e. connecting a timeline or similarily suitable model element to such a viewer. Actually, this connection is modelled by attaching to a BindingMO which is linked to a ViewerAsset. This way, the model tracks and persists the available viewer windows and the current connection situation.
 
 When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.
From 4731afefb83e47282d248d90976672d9fcc6d6f4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 25 May 2011 02:41:04 +0200 Subject: [PATCH 014/296] continue drafting the Player control elements -- life timevalue changes --- src/include/play-facade.h | 17 ++++++- src/lib/time/control.hpp | 102 ++++++++++++++++++++++++++++++++++++++ wiki/renderengine.html | 23 +++++---- 3 files changed, 130 insertions(+), 12 deletions(-) create mode 100644 src/lib/time/control.hpp diff --git a/src/include/play-facade.h b/src/include/play-facade.h index 82af1fddb..01db5f5f5 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -92,9 +92,22 @@ namespace lumiera { { public: void play(bool); ///< play/pause toggle + void scrub(bool); ///< scrubbing playback void adjustSpeed(double); ///< playback speed control - void go(lib::time::Time); - /////////////////////////////TODO how to modify the Loop range, the playback mode, scrubbing? + void go(lib::time::Time); ///< skip to the given point in time + + void controlPlayhead (lib::time::TimeControl & ctrl); + void controlDuration (lib::time::TimeControl & ctrl); + void controlLooping (lib::time::TimeControl & ctrl); + + void useProxyMedia (bool); + void setQuality (uint); + + bool is_playing() const; + bool is_scrubbing() const; + double getSpeed() const; + uint getQuality() const; + bool usesProxy() const; }; diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp new file mode 100644 index 000000000..5422daf45 --- /dev/null +++ b/src/lib/time/control.hpp @@ -0,0 +1,102 @@ +/* + CONTROL.hpp - a life time control for feedback and mutation + + Copyright (C) Lumiera.org + 2011, 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 control.hpp + ** Manipulating and monitoring time entities with life changes. + ** This is an control- and callback element to handle any kind of "running" + ** time entities. This element is to be provided by the client and then attached + ** to the target time entity as a time::Mutation. Internally, a life connection to + ** the target is built, allowing both to + ** - to manipulate the target by invoking the function operator + ** - to receive change notifications by installing a callback functor. + ** + ** The actual type of the changes and modifications is specified as template parameter; + ** when later attached to some time entity as a Mutation, the actual changes to be performed + ** depend both on this change type and the type of the target time entity (double dispatch). + ** The behaviour is similar to applying a static time::Mutation + ** + ** \par relevance + ** This control element is intended to be used for all kinds of editing and monitoring + ** of time-like entities -- be it the running time display in a GUI widget, a ruler marker + ** which can be dragged, a modifiable selection or the animated playhead cursor. + ** + ** @todo WIP-WIP-WIP + ** + */ + +#ifndef LIB_TIME_CONTROL_H +#define LIB_TIME_CONTROL_H + +#include "lib/error.hpp" +#include "lib/time/mutation.hpp" +//#include "lib/symbol.hpp" + +//#include +//#include +//#include +#include +//#include + + +namespace lib { +namespace time { + +//using lib::Symbol; +//using std::string; +//using lib::Literal; + using std::tr1::function; + +//LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated + + typedef function timeSignal; + + /** + * Interface: controller-element for retrieving and + * changing running time values + * + * @see time::Mutation + * @see time::TimeSpan#accept(Mutation const&) + * @todo WIP-WIP-WIP + */ + template + class Control + : Mutation + , timeSignal + { + public: + /** install a callback functor to be invoked + * to notify for any changes to the observed + * time entity */ + void connectChangeNotification (timeSignal); + + /** disconnect from observed entity and + * cease any change notification */ + void disconnnect(); + + }; + + + + +}} // lib::time +#endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index b5da538ca..1a36e067e 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6782,7 +6782,7 @@ Thus no server and no network connection is needed. Simply open the file in your * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
-
+
Simple time points are just like values and thus easy to change. The difficulties arise when time values are to be //quantised to an existing time grid.// The first relevant point to note is that for quantised time values, the effect of a change can be disproportional to the cause. Small changes below the threshold might be accumulated, and a tiny change might trigger a jump to the next grid point. While this might be annoying, the yet more complex questions arise when we acknowledge that the amount of the change itself might be related to a time grid.
 
 The problem with modification of quantised values highlights an inner contradiction or conflicting goals within the design
@@ -6843,27 +6843,30 @@ As rationale, consider the following
 * by materialising a quantised change prior to applying it, we get the "chaining effect"
 
 ----------
-!!! solutions ideas{{red{WIP 12/10}}}
+this leads to the following solutions approach
 * time values are immutable (as far as possible)
-* only allow to assign a completely new setting
-* only accept raw time values for redefining a quantised interval
-* only accept an abstract //modification object.// &rarr;{{red{No! any kind of abstraction requires an indirection}}}
+* for simple time calculations the ~TimeVar is sufficient
+* but we allow to fed an abstract //modification object.//
+* ''nudge by unit(s)'' is subsumed here as a special case
 
-{{red{Question 3/11}}} -- can we avoid accepting mutations for Time (and thus only mutate ~TimeSpan)?
+As the actual change logic to apply depends both on the target value and the kind of the change, we end up with //double dispatch.//
+
+ -- can we avoid accepting mutations for Time (and thus only mutate ~TimeSpan)?
 Rationale: allowing mutations for Time bears the danger of making ~TimeVar obsolete
 * //4/11 yes, implementing it now this way....//
 * we have ~TimeVar, so the only thing that //needs// to be mutable is a whole object &rArr; ~TimeSpan
 * err, because MObject will include a Duration separate from the start time in the Placement, Duration needs to be mutable too
 
 !!!usage considerations
-Question is: how fine grained and configurable needs this to be?
-* for example, when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
-* likely we need a "nudge by unit(s)"
+{{red{Question 5/11}}} when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
 
-!!! design draft
+!!!design draft
 The special focus of this problem seems to lead itself to a __visitor pattern__ based implementation. Because the basic hierarchy of applicable types is fixed, but the behaviour is open ended (and not yet fully determined). A conventional implementation would scatter this behaviour over all the time entities, thus making it hard to understand and reason about. The classical ~GoF cyclic visitor solution to the contrary allows us to arrange closely related behaviour into thematically grouped visitor classes. As a plus, the concrete action can be bound dynamically, allowing for more flexibility when it comes to dealing with the intricate situations when a quantised time span (= a clip) recieves a quantised mutation (= is re-alligend to a possibly different frame grid)
 
 Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}}} function, which is defined to reflect back into the (virtual) visitation functions of the Mutation Interface. Additionally, we provide some static convenience shortcuts right in the Mutation interface, performing the trivial mutations.
+
+!!!life value changes and monitoring
+Based on this time::Mutation design, we provide a specialised element for dealing with //running time values:// When attached to a target time entity, a "life" connection is established. From then on, continuous changes and mutations can be fed to the target by invoking a functor interface. Besides, a change notification signal (callback) can be installed, which will be invoked on each change. This element is the foundation for implementing all kinds of running time display widgets, spin buttons, timeline selections, playheads, loop playback and similar.
 
From 9004818d6353aac9154a3f2dc765174b23834e1a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 26 May 2011 03:05:52 +0200 Subject: [PATCH 015/296] further planning the features of time::Change --- src/lib/time/control.hpp | 11 +++ src/proc/play/play-service.hpp | 1 + tests/40components.tests | 5 + tests/lib/time/time-control-test.cpp | 137 +++++++++++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 tests/lib/time/time-control-test.cpp diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 5422daf45..29a16023a 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -40,6 +40,17 @@ ** of time-like entities -- be it the running time display in a GUI widget, a ruler marker ** which can be dragged, a modifiable selection or the animated playhead cursor. ** + ** \par implementation notes + ** - the validity of a given combination of change and target is checked immediately, + ** when connecting to the target. Depending on the situation, the actual changes later + ** are subject to specific treatment (e.g. frame quantisation) + ** - by default time::Control is not threadsafe. But, as each change is basically + ** processed within its own call context (function invocation), parallelism is only + ** a concern with respect to the value finally visible within the target. + ** - the change notification is processed right away, after applying the change to the + ** target; in all cases, the effective change value is what will be propagated, \em not + ** the content of the target after applying the change + ** ** @todo WIP-WIP-WIP ** */ diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index ee9a04188..f2b8e2d4d 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -33,6 +33,7 @@ #include "lib/error.hpp" +#include "include/play-facade.h" #include //#include diff --git a/tests/40components.tests b/tests/40components.tests index 22defbc85..aff1800f8 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -688,6 +688,11 @@ return: 0 END +PLANNED "Life changing time specifications with feedback" TimeControl_test < + + 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/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/timequant.hpp" +#include "lib/time/mutation.hpp" +#include "proc/asset/meta/time-grid.hpp" +#include "lib/util.hpp" + +#include +#include +#include + +using boost::lexical_cast; +using util::isnil; +using std::cout; +using std::endl; +using std::string; + + +namespace lib { +namespace time{ +namespace test{ + + using asset::meta::TimeGrid; + + namespace { + inline string + pop (Arg arg) + { + if (isnil (arg)) return ""; + string entry = arg[0]; + arg.erase (arg.begin()); + return entry; + } + } + + + + + /**************************************************************** + * @test use the time::Control to push a sequence of modifications + * to various time entities; in all cases, a suitable change + * should be imposed to the target and then a notification signal + * should be invoked. + * @todo cover the cases..... + * - change to a given value + * - change by an offset + * - change using a grid value + * - apply an (grid) increment + */ + class TimeControl_test : public Test + { + gavl_time_t + random_or_get (string arg) + { + if (isnil(arg)) + return gavl_time_t (1 + (rand() % 100000)) * GAVL_TIME_SCALE; + else + return lexical_cast (arg); + } + + struct TestValues + { + TimeVar var; + Duration dur; + TimeSpan span; + QuTime quant; + + TestValues (TimeValue o) + : var(o) + , dur(o) + , span(o, Offset(o)) + , quant(o, "test_grid") + { } + }; + + + virtual void + run (Arg arg) + { + TimeValue o (random_or_get (pop(arg))); + TimeValue c (random_or_get (pop(arg))); + CHECK (o != c, "unsuitable testdata"); + + // using a 25fps-grid, but with an time origin offset by 1/50sec + TimeGrid::build("test_grid", FrameRate::PAL, Time(FSecs(1,50))); + + QuTime qChange (c, "test_grid"); + FrameNr count(qChange); + + verifyBasics(); + runningMutations(); + } + + + void + verifyBasics() + { + } + + + void + runningMutations () + { + } + }; + + + /** Register this test class... */ + LAUNCHER (TimeControl_test, "unit common"); + + + +}}} // namespace lib::time::test From eb5e0ab9ff8951d56d8cd265accd16ea5d484396 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 27 May 2011 04:16:36 +0200 Subject: [PATCH 016/296] WIP: start building a Test for time::Control. Need Cartesian product of cases... --- src/lib/meta/generator-combinations.hpp | 106 ++++++++++++++++++++++++ src/lib/meta/typelist-util.hpp | 66 ++++++++++++--- tests/40components.tests | 2 +- tests/lib/meta/typelist-diagnostics.hpp | 8 +- tests/lib/meta/typelist-manip-test.cpp | 4 +- tests/lib/time/time-control-test.cpp | 21 ++++- wiki/renderengine.html | 4 +- 7 files changed, 190 insertions(+), 21 deletions(-) create mode 100644 src/lib/meta/generator-combinations.hpp diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp new file mode 100644 index 000000000..a74eaea06 --- /dev/null +++ b/src/lib/meta/generator-combinations.hpp @@ -0,0 +1,106 @@ +/* + GENERATOR-COMBINATIONS.hpp - generate combinations and variations + + Copyright (C) Lumiera.org + 2011, 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 generator-combinations.hpp + ** Metaprogramming facilities to generate combination cases. + ** Similar to the plain typelist-based generators, a custom supplied + ** template will be instantiated with combinations of the parameter types + ** and then mixed into the resulting type + ** + ** @see generator-combinations-test.cpp + ** @see generator.hpp + ** + */ + + +#ifndef LUMIERA_META_GENERATOR_COMBINATIONS_H +#define LUMIERA_META_GENERATOR_COMBINATIONS_H + +#include "lib/meta/typelist.hpp" +#include "lib/meta/typelist-util.hpp" +#include "lib/meta/generator.hpp" + + + +namespace lumiera { +namespace typelist{ + + + template + class CartesianProduct + : Distribute< typename TYPES_1::List + , typename TYPES_2::List + > + { }; + + + template + struct PickFirst; + + template + struct PickFirst > + { + typedef TY Type; + }; + + + template + struct PickSecond; + + template + struct PickSecond > + { + typedef typename PickFirst::Type Type; + }; + + + + template< template class _X_> + struct PickParametersFromSublist + { + template + struct CaseInstantiation + : _X_< PickFirst::Type + , PickSecond::Type + , BASE + > + { }; + }; + + + template + < class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from + , template class _X_ ///< template with two arg types and a base type + , class BASE = NullType + > + class InstantiateChainedCombinations + : InstantiateChained< CartesianProduct::List + , PickParametersFromSublist<_X_>::template CaseInstantiation + , BASE + > + { }; + + + +}} // namespace lumiera::typelist +#endif diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp index cf66a791a..55fdd4395 100644 --- a/src/lib/meta/typelist-util.hpp +++ b/src/lib/meta/typelist-util.hpp @@ -21,6 +21,34 @@ */ +/** @file typelist-util.hpp + ** Metaprogramming: Helpers for manipulating lists-of-types. + ** Sometimes, we use metaprogramming to generate a variation of concrete + ** implementations by combining some basic building blocks. Typically, there + ** is a number of similar, but not suitably related types involved. We want to + ** process those types using a common scheme, without being forced to squeeze + ** all those types into a artificial inheritance relationship. Now, generating + ** some kind of common factory or adapter, while mixing in pieces of code tailored + ** specifically to the individual types, allows still to build a common processing + ** in such situations. + ** + ** The facilities in this header provide the basics of simple functional list + ** processing (mostly with tail recursion). Usually, there is one template parameter + ** TYPES, which accepts a \em Type-list. The result of the processing step is then + ** accessible as an embedded typedef named \c List . Here, all of the 'processing' + ** to calculate this result is performed by the compiler, as a side-effect of + ** figuring out the resulting concrete type. At run time, in the generated + ** code, typically the resulting classes are empty, maybe just + ** exposing a specifically built-up function. + ** + ** @see generator.hpp + ** @see typelist-manip-test.cpp + ** @see TimeControl_test usage example + ** @see typelist.hpp + ** + */ + + #ifndef LUMIERA_META_TYPELIST_UTIL_H #define LUMIERA_META_TYPELIST_UTIL_H @@ -34,6 +62,7 @@ namespace typelist{ /** * Metafunction counting the number of Types in the collection + * @return an embedded constant \c value holding the result */ template struct count; @@ -80,6 +109,7 @@ namespace typelist{ > List; }; + /** conditional node: skip an element based on evaluating a predicate */ template struct CondNode { typedef TAIL Next; }; @@ -202,7 +232,7 @@ namespace typelist{ typedef T Head; ///< first element typedef Node First; ///< a list containing the first element typedef TYPES Tail; ///< remainder of the list starting with the second elm. - typedef typename SplitLast::List Prefix;///< all of the list, up to but extcluding the last element + typedef typename SplitLast::List Prefix;///< all of the list, up to but excluding the last element typedef typename SplitLast::Type End; ///< the last element typedef Node Last; ///< a list containing the last element }; @@ -245,6 +275,12 @@ namespace typelist{ + /** + * build a list-of lists, where each element of the first arg list + * gets in turn prepended to all elements of the second arg list. + * Can be used to build all possible combinations from two + * sources, i.e. the Cartesian product. + */ template struct Distribute { typedef typename PrefixAll::List List; }; @@ -261,24 +297,32 @@ namespace typelist{ - /** use a permutation generator - * for creating a list of all possible combinations + /** + * build all possible combinations, based on a enumeration of the basic cases. + * For each of the types in the argument list, an "enumeration generator" template is invoked, + * yielding a list of the possible base cases. These base cases are then combined with all the + * combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered" + * means that the base cases of the n-th element will appear in the n-th position of the + * resulting lists, + * + * For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a + * list-of-lists, featuring all possibilities to combine these distinct toggles. */ template< class X - , template class _PERMU_> - struct Combine { typedef typename Distribute< typename _PERMU_::List + , template class _ENUM_> + struct Combine { typedef typename Distribute< typename _ENUM_::List , Node >::List List; }; - template< template class _PERMU_> - struct Combine { typedef Node List; }; + template< template class _ENUM_> + struct Combine { typedef NodeNull List; }; template< class TY, class TYPES - , template class _PERMU_> - struct Combine,_PERMU_> { typedef typename Distribute< typename _PERMU_::List - , typename Combine::List + , template class _ENUM_> + struct Combine,_ENUM_> { typedef typename Distribute< typename _ENUM_::List + , typename Combine::List >::List List; }; - /** permutation generator for the Combine metafunction, + /** enumeration generator for the Combine metafunction, * yielding an "on" and "off" case */ template diff --git a/tests/40components.tests b/tests/40components.tests index aff1800f8..3405644a7 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -806,7 +806,7 @@ return: 0 END -TEST "typelist manipulation" TypeListManipl_test <-<2>-<3>- out: List2 :-<5>-<6>-<7>- out: Added2 :-<3>-<4>-<5>- diff --git a/tests/lib/meta/typelist-diagnostics.hpp b/tests/lib/meta/typelist-diagnostics.hpp index bd924b1d8..b26eb7ae7 100644 --- a/tests/lib/meta/typelist-diagnostics.hpp +++ b/tests/lib/meta/typelist-diagnostics.hpp @@ -25,13 +25,15 @@ ** a Printer template usable for debugging the structure of a typelist built ** upon some simple debugging-style types. Examples being a Num template, ** or the Flag type. A Printer type generated from this template provides - ** a static \c print() function returning a string visualising the structure - ** of the typelist provided as parameter to the Printer template. - ** + ** a static \c print() function. The string returned from this function + ** visualises the structure of the typelist provided as parameter + ** to the Printer template. + ** ** @see typelist-manip-test.cpp ** @see config-flags-test.cpp ** */ + #ifndef META_TYPELIST_DIAGNOSTICS_H #define META_TYPELIST_DIAGNOSTICS_H diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index ce51270a5..9bf43c429 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -90,7 +90,7 @@ namespace test { * using a "predicate template" (metafunction) * - building combinations and permutations */ - class TypeListManipl_test : public Test + class TypeListManip_test : public Test { virtual void run (Arg) @@ -352,7 +352,7 @@ namespace test { /** Register this test class... */ - LAUNCHER (TypeListManipl_test, "unit common"); + LAUNCHER (TypeListManip_test, "unit common"); diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index 2742831ce..b233ef114 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -27,6 +27,7 @@ #include "lib/time/timequant.hpp" #include "lib/time/mutation.hpp" #include "proc/asset/meta/time-grid.hpp" +#include "lib/meta/typelist.hpp" #include "lib/util.hpp" #include @@ -45,6 +46,8 @@ namespace time{ namespace test{ using asset::meta::TimeGrid; + using lumiera::typelist::Types; + using lumiera::typelist::InstantiateForEach; namespace { inline string @@ -112,7 +115,7 @@ namespace test{ FrameNr count(qChange); verifyBasics(); - runningMutations(); + verifyMatrix_of_MutationCases(); } @@ -121,10 +124,24 @@ namespace test{ { } + template + struct TestCase4Target + { + void + performTestCases() + { + + } + }; + void - runningMutations () + verifyMatrix_of_MutationCases () { + typedef Types KindsOfTarget; + typedef InstantiateForEach TestMatrix; + + TestMatrix().performTestCases(); } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 1a36e067e..83ca6e30c 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6782,7 +6782,7 @@ Thus no server and no network connection is needed. Simply open the file in your * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
-
+
Simple time points are just like values and thus easy to change. The difficulties arise when time values are to be //quantised to an existing time grid.// The first relevant point to note is that for quantised time values, the effect of a change can be disproportional to the cause. Small changes below the threshold might be accumulated, and a tiny change might trigger a jump to the next grid point. While this might be annoying, the yet more complex questions arise when we acknowledge that the amount of the change itself might be related to a time grid.
 
 The problem with modification of quantised values highlights an inner contradiction or conflicting goals within the design
@@ -6866,7 +6866,7 @@ The special focus of this problem seems to lead itself to a __visitor pattern__
 Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}}} function, which is defined to reflect back into the (virtual) visitation functions of the Mutation Interface. Additionally, we provide some static convenience shortcuts right in the Mutation interface, performing the trivial mutations.
 
 !!!life value changes and monitoring
-Based on this time::Mutation design, we provide a specialised element for dealing with //running time values:// When attached to a target time entity, a "life" connection is established. From then on, continuous changes and mutations can be fed to the target by invoking a functor interface. Besides, a change notification signal (callback) can be installed, which will be invoked on each change. This element is the foundation for implementing all kinds of running time display widgets, spin buttons, timeline selections, playheads, loop playback and similar.
+Based on this time::Mutation design, we provide a specialised element for dealing with //running time values:// When attached to a target time entity, a "life" connection is established. From then on, continuous changes and mutations can be fed to the target by invoking a functor interface. Besides, a change notification signal (callback) can be installed, which will be invoked on each change. This {{{time::Control}}} element is the foundation for implementing all kinds of running time display widgets, spin buttons, timeline selections, playheads, loop playback and similar.
 
From 786ecbe82944658cf2686ecdccd3d98416f33725 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 27 May 2011 21:27:55 +0200 Subject: [PATCH 017/296] WIP draft test helper for generating test cases (Cartesian product) --- src/lib/meta/generator-combinations.hpp | 45 +++--- src/lib/meta/typelist-util.hpp | 20 +++ src/lib/meta/util.hpp | 18 +++ tests/40components.tests | 5 + .../lib/meta/generator-combinations-test.cpp | 137 ++++++++++++++++++ tests/lib/meta/typelist-diagnostics.hpp | 4 +- tests/lib/meta/typelist-manip-test.cpp | 27 +++- 7 files changed, 226 insertions(+), 30 deletions(-) create mode 100644 tests/lib/meta/generator-combinations-test.cpp diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp index a74eaea06..dae553182 100644 --- a/src/lib/meta/generator-combinations.hpp +++ b/src/lib/meta/generator-combinations.hpp @@ -25,7 +25,7 @@ ** Metaprogramming facilities to generate combination cases. ** Similar to the plain typelist-based generators, a custom supplied ** template will be instantiated with combinations of the parameter types - ** and then mixed into the resulting type + ** and then mixed into the resulting type ** ** @see generator-combinations-test.cpp ** @see generator.hpp @@ -54,40 +54,35 @@ namespace typelist{ { }; - template - struct PickFirst; - - template - struct PickFirst > - { - typedef TY Type; - }; - - - template - struct PickSecond; - - template - struct PickSecond > - { - typedef typename PickFirst::Type Type; - }; - - template< template class _X_> struct PickParametersFromSublist { template - struct CaseInstantiation - : _X_< PickFirst::Type - , PickSecond::Type + struct SingleCaseInstantiation + : _X_< Pick::Type + , Pick::Type , BASE > { }; }; + /** + * Build a Case matrix. + * The given parameter template _X_ + * will be instantiated for each possible combination + * of the elements from both parameter type-lists. + * All these instantiations will be chained up + * into a linear inheritance chain rooted + * at the BASE type. + * @note the custom-supplied template _X_ needs to take a 3rd parameter, + * and inherit from this parameter, in order to form that chain. + * Typically you'll define some (static) functions within that + * template, which then forward the call to the given BASE + * (and of course, that BASE then needs to define this + * function as well). + */ template < class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from , template class _X_ ///< template with two arg types and a base type @@ -95,7 +90,7 @@ namespace typelist{ > class InstantiateChainedCombinations : InstantiateChained< CartesianProduct::List - , PickParametersFromSublist<_X_>::template CaseInstantiation + , PickParametersFromSublist<_X_>::template SingleCaseInstantiation , BASE > { }; diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp index 55fdd4395..428c23f97 100644 --- a/src/lib/meta/typelist-util.hpp +++ b/src/lib/meta/typelist-util.hpp @@ -97,6 +97,26 @@ namespace typelist{ }; + /** pick the n-th element from a typelist */ + template + struct Pick + { + typedef NullType Type; + }; + template + struct Pick, 0> + { + typedef TY Type; + }; + template + struct Pick, i> + { + typedef typename Pick::Type Type; + }; + + + + /** apply a transformation (template) to each type in the list */ template class _TRANS_> struct Apply { typedef TY List; }; diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index e6bb82209..97e60f1e1 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -38,6 +38,24 @@ namespace lumiera { namespace typelist { + /** Compile-time Type equality: + * Simple Trait template to pick up types considered + * \em identical by the compiler. + * @warning identical, not sub-type! + */ + template + class is_sameType + { + static const bool value = false; + }; + + template + class is_sameType + { + static const bool value = true; + }; + + /** semi-automatic detection if an instantiation is possible. * Requires help by the template to be tested, which needs to define * a typedef member \c is_defined. The embedded metafunction Test can be used diff --git a/tests/40components.tests b/tests/40components.tests index 3405644a7..340203dbf 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -1104,6 +1104,11 @@ return: 0 END +TEST "Typelist combinations generator" GeneratorCombinations_test < + + 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/test/run.hpp" +#include "lib/meta/generator.hpp" +#include "lib/meta/generator-combinations.hpp" +#include "meta/typelist-diagnostics.hpp" + +#include +#include + +using ::test::Test; +using std::string; +using std::cout; +using std::endl; + + +namespace lumiera { +namespace typelist{ +namespace test { + + + namespace { // test cases and data.... + + typedef Types< Num<1> + , Num<3> + , Num<5> + > Types1; + typedef Types< Num<2> + , Num<4> + , Num<6> + > Types2; + + + using boost::str; + using boost::format; + format fmt ("-<%u%u>%s"); + + /** + * A Test-Template to be instantiated + * for all possible combinations + * of the {Types1} x {Types2} + */ + template + struct TestCase + { + static string + visitAll() + { + T1 param1; + T2 param2; + return str(fmt % param1 % param2 + % BASE::visitAll()); + } + }; + + struct TestCase + { + static string + visitAll() + { + return "-|"; + } + }; + typedef TestCase IterationEnd; + + } // (End) test data + + + + + + + /************************************************************************* + * @test check utilities for generating case combinations. + * - verify the Cartesian product is built properly + * - instantiate a two-parameter test template + * for all those cases, as given by the + * Cartesian product of two Type collections + */ + class GeneratorCombinations_test : public Test + { + virtual void + run (Arg) + { + checkCartesian(); + checkCaseInstantiation(); + } + + + void + checkCartesian () + { + typedef CartesianProduct Cartesian; + DISPLAY (Cartesian); + } + + + void + checkCaseInstantiation () + { + typedef InstantiateChainedCombinations< Types1,Types2 + , TestCase + , IterationEnd > CombnationCases; + + cout << "All-Test-Combinations-" << CombnationCases::visitAll() << endl; + } + + }; + + + /** Register this test class... */ + LAUNCHER (GeneratorCombinations_test, "unit common"); + + + +}}} // namespace lumiera::typelist::test diff --git a/tests/lib/meta/typelist-diagnostics.hpp b/tests/lib/meta/typelist-diagnostics.hpp index b26eb7ae7..10e64154a 100644 --- a/tests/lib/meta/typelist-diagnostics.hpp +++ b/tests/lib/meta/typelist-diagnostics.hpp @@ -200,10 +200,10 @@ namespace typelist{ #define DISPLAY(NAME) \ - cout << STRINGIFY(NAME) << "\t:" << showType() << "\n"; + cout << STRINGIFY(NAME) << "\t:" << showType() << endl; #define DUMPVAL(NAME) \ - cout << STRINGIFY(NAME) << "\t:" << showDump (NAME) << "\n"; + cout << STRINGIFY(NAME) << "\t:" << showDump (NAME) << endl; diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index 9bf43c429..3e34eab37 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -43,12 +43,11 @@ #include "lib/meta/typelist-util.hpp" #include "meta/typelist-diagnostics.hpp" -#include #include using ::test::Test; -using std::string; using std::cout; +using std::endl; namespace lumiera { @@ -96,6 +95,7 @@ namespace test { run (Arg) { check_diagnostics (); + check_pick_elm (); check_apply (); check_filter (); check_append (); @@ -113,7 +113,7 @@ namespace test { { // Explanation: the DISPLAY macro results in the following definition.... typedef InstantiateChained Contents_List1; - cout << "List1" << "\t:" << Contents_List1::print() << "\n"; + cout << "List1" << "\t:" << Contents_List1::print() << endl; // That is: we instantiate the "Printer" template for each of the types in List1, // forming an inheritance chain. I.e. the defined Type "Contents_List1" inherits @@ -124,6 +124,27 @@ namespace test { } + void + check_pick_elm () + { + Pick::Type e0; + Pick::Type e1; + Pick::Type e2; + + Pick::Type E3; + Pick::Type Nil; + Pick::Type Irrelevant; + + CHECK (5 == e0); + CHECK (6 == e1); + CHECK (7 == e2); + + CHECK (is_sameType::value); + CHECK (is_sameType::value); + CHECK (is_sameType::value); + } + + void check_append () { From ae36b2d941656faf63487e962f6b1ecc33ed7dca Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 May 2011 01:46:06 +0200 Subject: [PATCH 018/296] stubs and adjustments to get it through the compiler --- src/include/interfaceproxy.hpp | 72 +++-- src/include/play-facade.h | 18 +- src/lib/meta/generator-combinations.hpp | 10 +- src/lib/meta/util.hpp | 4 +- src/lib/time/control.hpp | 7 +- src/proc/engine/dispatcher.hpp | 6 +- src/proc/engine/engine-service.hpp | 28 +- .../engine/worker/dummy-image-generator.cpp | 282 +++++++++--------- .../engine/worker/dummy-image-generator.hpp | 86 +++--- src/proc/engine/worker/tick-service.hpp | 148 +++++---- src/proc/play/dummy-play-connection.hpp | 38 ++- src/proc/play/dummy-player-service.cpp | 5 +- src/proc/play/dummy-player-service.hpp | 9 +- src/proc/play/output-manager.cpp | 5 +- src/proc/play/play-process.hpp | 54 ++-- src/proc/play/play-service.cpp | 95 ++---- src/proc/play/play-service.hpp | 58 ++-- tests/lib/meta/config-flags-test.cpp | 1 + .../lib/meta/generator-combinations-test.cpp | 8 +- tests/lib/meta/typelist-manip-test.cpp | 12 +- tests/lib/meta/typeseq-manip-test.cpp | 1 + tests/lib/time/time-control-test.cpp | 37 ++- 22 files changed, 474 insertions(+), 510 deletions(-) diff --git a/src/include/interfaceproxy.hpp b/src/include/interfaceproxy.hpp index a488ce65f..54084f8fd 100644 --- a/src/include/interfaceproxy.hpp +++ b/src/include/interfaceproxy.hpp @@ -82,45 +82,43 @@ namespace lumiera { - namespace facade { - - /** error-ID for accessing a (currently) closed facade */ - LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE); +namespace facade { + /** error-ID for accessing a (currently) closed facade */ + LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE); + + + /********************************************************************* + * + */ + template + class Accessor + { + protected: + static FA* implProxy_; + + + public: + FA& + operator() () + { + if (implProxy_) + return *implProxy_; + else + throw error::State("Facade interface currently closed."); + } + }; - /********************************************************************* - * - */ - template - class Accessor - { - protected: - static FA* implProxy_; - - - public: - FA& - operator() () - { - if (implProxy_) - return *implProxy_; - else - throw error::State("Facade interface currently closed."); - } - }; - - template - void openProxy (IHA const&); - - template - void closeProxy (); - - template - class Proxy; - - - } // namespace facade + template + void openProxy (IHA const&); -} // namespace lumiera + template + void closeProxy (); + + template + class Proxy; + + +}} // namespace lumiera::facade #endif diff --git a/src/include/play-facade.h b/src/include/play-facade.h index 01db5f5f5..33429323b 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -32,14 +32,16 @@ //#include "include/interfaceproxy.hpp" #include "lib/handle.hpp" #include "lib/iter-source.hpp" +#include "lib/time/control.hpp" #include "lib/time/timevalue.hpp" +#include "include/interfaceproxy.hpp" #include "proc/mobject/model-port.hpp" #include "proc/mobject/output-designation.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/track.hpp" #include "proc/play/output-manager.hpp" -#include "asset/timeline.hpp" -#include "asset/viewer.hpp" +#include "proc/asset/timeline.hpp" +#include "proc/asset/viewer.hpp" @@ -51,6 +53,8 @@ namespace proc { namespace lumiera { + + namespace time = lib::time; /****************************************************************** @@ -71,7 +75,7 @@ namespace lumiera { public: /** get an implementation instance of this service */ - static lumiera::facade::Accessor facade; + static lumiera::facade::Accessor facade; /** @@ -96,9 +100,9 @@ namespace lumiera { void adjustSpeed(double); ///< playback speed control void go(lib::time::Time); ///< skip to the given point in time - void controlPlayhead (lib::time::TimeControl & ctrl); - void controlDuration (lib::time::TimeControl & ctrl); - void controlLooping (lib::time::TimeControl & ctrl); + void controlPlayhead (time::Control & ctrl); + void controlDuration (time::Control & ctrl); + void controlLooping (time::Control & ctrl); void useProxyMedia (bool); void setQuality (uint); @@ -131,7 +135,7 @@ namespace lumiera { Controller perform(Clip); protected: - virtual ~Player(); + virtual ~Play(); }; diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp index dae553182..28790ec01 100644 --- a/src/lib/meta/generator-combinations.hpp +++ b/src/lib/meta/generator-combinations.hpp @@ -47,7 +47,7 @@ namespace typelist{ template - class CartesianProduct + struct CartesianProduct : Distribute< typename TYPES_1::List , typename TYPES_2::List > @@ -60,8 +60,8 @@ namespace typelist{ { template struct SingleCaseInstantiation - : _X_< Pick::Type - , Pick::Type + : _X_< typename Pick::Type + , typename Pick::Type , BASE > { }; @@ -88,8 +88,8 @@ namespace typelist{ , template class _X_ ///< template with two arg types and a base type , class BASE = NullType > - class InstantiateChainedCombinations - : InstantiateChained< CartesianProduct::List + struct InstantiateChainedCombinations + : InstantiateChained< typename CartesianProduct::List , PickParametersFromSublist<_X_>::template SingleCaseInstantiation , BASE > diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 97e60f1e1..14c7cd6a8 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -44,13 +44,13 @@ namespace lumiera { * @warning identical, not sub-type! */ template - class is_sameType + struct is_sameType { static const bool value = false; }; template - class is_sameType + struct is_sameType { static const bool value = true; }; diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 29a16023a..e5f44629c 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -79,7 +79,6 @@ namespace time { //LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated - typedef function timeSignal; /** * Interface: controller-element for retrieving and @@ -91,10 +90,12 @@ namespace time { */ template class Control - : Mutation - , timeSignal + : public Mutation + , public function { public: + typedef function timeSignal; + /** install a callback functor to be invoked * to notify for any changes to the observed * time entity */ diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp index 5b4509953..7565c70eb 100644 --- a/src/proc/engine/dispatcher.hpp +++ b/src/proc/engine/dispatcher.hpp @@ -32,9 +32,9 @@ namespace engine { -// using lib::time::TimeSpan; -// using lib::time::FSecs; -// using lib::time::Time; + using lib::time::TimeSpan; + using lib::time::FSecs; + using lib::time::Time; // // class ExitNode; diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index 599f4fe40..7798307b0 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -37,7 +37,7 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" // -//#include +#include //#include //#include @@ -66,31 +66,23 @@ namespace play { : boost::noncopyable { - string error_; - Subsys::SigTerm notifyTermination_; +// string error_; +// Subsys::SigTerm notifyTermination_; /* === Interface Lifecycle === */ - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; +// typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) +// , DummyPlayer +// > ServiceInstanceHandle; - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; +// lib::SingletonRef implInstance_; +// ServiceInstanceHandle serviceInstance_; public: - DummyPlayerService(Subsys::SigTerm terminationHandle); + EngineService(); /////TODO (Subsys::SigTerm terminationHandle); - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); + ~EngineService() { } /////TODO notifyTermination_(&error_); } }; diff --git a/src/proc/engine/worker/dummy-image-generator.cpp b/src/proc/engine/worker/dummy-image-generator.cpp index 44cf97cd1..577da2f94 100644 --- a/src/proc/engine/worker/dummy-image-generator.cpp +++ b/src/proc/engine/worker/dummy-image-generator.cpp @@ -22,149 +22,147 @@ * *****************************************************/ -#include "proc/play/dummy-image-generator.hpp" +#include "proc/engine/worker/dummy-image-generator.hpp" namespace proc { - namespace play { - - - - namespace { // implementation details - - - typedef unsigned char byte; - - inline int - clamp (const int &val, const int &maxval, const int &minval) - { - if(val > maxval) return maxval; - if(val < minval) return minval; - return val; - } - - inline void - rgb_to_yuv (int r, int g, int b, byte &y, byte &u, byte &v) - { - // This code isn't great, but it does the job - y = (byte)clamp((299 * r + 587 * g + 114 * b) / 1000, 235, 16); - v = (byte)clamp((500 * r - 419 * g - 81 * b) / 1000 + 127, 255, 0); - u = (byte)clamp((-169 * r - 331 * g + 500 * b) / 1000 + 127, 255, 0); - } - - - void - rgb_buffer_to_yuy2 (unsigned char *in, unsigned char *out) - { - for (uint i = 0; i < 320*240*2; i+=4) - { - byte y0, u0, v0; - const byte r0 = *(in++); - const byte g0 = *(in++); - const byte b0 = *(in++); - rgb_to_yuv(r0, g0, b0, y0, u0, v0); - - byte y1, u1, v1; - const byte r1 = *(in++); - const byte g1 = *(in++); - const byte b1 = *(in++); - rgb_to_yuv(r1, g1, b1, y1, u1, v1); - - out[i] = y0; - out[i + 1] = u0; - out[i + 2] = y1; - out[i + 3] = v0; - } } - - - } // (End) implementation details - - - - - DummyImageGenerator::DummyImageGenerator(uint fps) - : current_(0) - , frame_(0) - , fps_(fps) - { } - - - LumieraDisplayFrame - DummyImageGenerator::next() - { - - ++frame_; - if(frame_ > 2 * fps_) - frame_ = 0; - - if(frame_ < 1 * fps_) - { - // create random snow... - for(int i = 0; i < 320*240*3; i+=3) - { - byte value ( rand() ); - buf_[i] = value; - buf_[i+1] = value; - buf_[i+2] = value; - } - } - else - { // create a colour strip pattern - typedef unsigned char Row[320 * 3]; - - unsigned char * row = buf_; - - // create a colour strip pattern in the first row... - for(int x = 0; x < 320; ++x) - { - byte &r = row[x*3]; - byte &g = row[x*3+1]; - byte &b = row[x*3+2]; - - if (x < 1*320/7) r = 0xC0, g = 0xC0, b = 0xC0; - else if(x < 2*320/7) r = 0xC0, g = 0xC0, b = 0x00; - else if(x < 3*320/7) r = 0x00, g = 0xC0, b = 0xC0; - else if(x < 4*320/7) r = 0x00, g = 0xC0, b = 0x00; - else if(x < 5*320/7) r = 0xC0, g = 0x00, b = 0xC0; - else if(x < 6*320/7) r = 0xC0, g = 0x00, b = 0x00; - else r = 0x00, g = 0x00, b = 0xC0; - } - - // fill remaining rows of the frame with the same pattern - for(int y = 1; y < 240; ++y) - memcpy(buf_ + y*sizeof(Row), row, sizeof(Row)); - - } - - // select output buffer to return - LumieraDisplayFrame outBuff; - - if (!current_) - { - outBuff = outFrame_A_; - current_= 1; - } - else - { - outBuff = outFrame_B_; - current_= 0; - } - - rgb_buffer_to_yuy2(buf_, outBuff); - return outBuff; - - } - - - LumieraDisplayFrame - DummyImageGenerator::current() - { - if (!current_) return outFrame_A_; - else return outFrame_B_; - } - - - - } // namespace play +namespace node { -} // namespace proc + + + namespace { // implementation details + + + typedef unsigned char byte; + + inline int + clamp (const int &val, const int &maxval, const int &minval) + { + if(val > maxval) return maxval; + if(val < minval) return minval; + return val; + } + + inline void + rgb_to_yuv (int r, int g, int b, byte &y, byte &u, byte &v) + { + // This code isn't great, but it does the job + y = (byte)clamp((299 * r + 587 * g + 114 * b) / 1000, 235, 16); + v = (byte)clamp((500 * r - 419 * g - 81 * b) / 1000 + 127, 255, 0); + u = (byte)clamp((-169 * r - 331 * g + 500 * b) / 1000 + 127, 255, 0); + } + + + void + rgb_buffer_to_yuy2 (unsigned char *in, unsigned char *out) + { + for (uint i = 0; i < 320*240*2; i+=4) + { + byte y0, u0, v0; + const byte r0 = *(in++); + const byte g0 = *(in++); + const byte b0 = *(in++); + rgb_to_yuv(r0, g0, b0, y0, u0, v0); + + byte y1, u1, v1; + const byte r1 = *(in++); + const byte g1 = *(in++); + const byte b1 = *(in++); + rgb_to_yuv(r1, g1, b1, y1, u1, v1); + + out[i] = y0; + out[i + 1] = u0; + out[i + 2] = y1; + out[i + 3] = v0; + } } + + + } // (End) implementation details + + + + + DummyImageGenerator::DummyImageGenerator(uint fps) + : current_(0) + , frame_(0) + , fps_(fps) + { } + + + LumieraDisplayFrame + DummyImageGenerator::next() + { + + ++frame_; + if(frame_ > 2 * fps_) + frame_ = 0; + + if(frame_ < 1 * fps_) + { + // create random snow... + for(int i = 0; i < 320*240*3; i+=3) + { + byte value ( rand() ); + buf_[i] = value; + buf_[i+1] = value; + buf_[i+2] = value; + } + } + else + { // create a colour strip pattern + typedef unsigned char Row[320 * 3]; + + unsigned char * row = buf_; + + // create a colour strip pattern in the first row... + for(int x = 0; x < 320; ++x) + { + byte &r = row[x*3]; + byte &g = row[x*3+1]; + byte &b = row[x*3+2]; + + if (x < 1*320/7) r = 0xC0, g = 0xC0, b = 0xC0; + else if(x < 2*320/7) r = 0xC0, g = 0xC0, b = 0x00; + else if(x < 3*320/7) r = 0x00, g = 0xC0, b = 0xC0; + else if(x < 4*320/7) r = 0x00, g = 0xC0, b = 0x00; + else if(x < 5*320/7) r = 0xC0, g = 0x00, b = 0xC0; + else if(x < 6*320/7) r = 0xC0, g = 0x00, b = 0x00; + else r = 0x00, g = 0x00, b = 0xC0; + } + + // fill remaining rows of the frame with the same pattern + for(int y = 1; y < 240; ++y) + memcpy(buf_ + y*sizeof(Row), row, sizeof(Row)); + + } + + // select output buffer to return + LumieraDisplayFrame outBuff; + + if (!current_) + { + outBuff = outFrame_A_; + current_= 1; + } + else + { + outBuff = outFrame_B_; + current_= 0; + } + + rgb_buffer_to_yuy2(buf_, outBuff); + return outBuff; + + } + + + LumieraDisplayFrame + DummyImageGenerator::current() + { + if (!current_) return outFrame_A_; + else return outFrame_B_; + } + + + +}} // namespace proc::node diff --git a/src/proc/engine/worker/dummy-image-generator.hpp b/src/proc/engine/worker/dummy-image-generator.hpp index c893962b8..e364423ef 100644 --- a/src/proc/engine/worker/dummy-image-generator.hpp +++ b/src/proc/engine/worker/dummy-image-generator.hpp @@ -35,8 +35,8 @@ */ -#ifndef PROC_PLAY_DUMMY_IMAGE_GENERATOR_H -#define PROC_PLAY_DUMMY_IMAGE_GENERATOR_H +#ifndef PROC_NODE_DUMMY_IMAGE_GENERATOR_H +#define PROC_NODE_DUMMY_IMAGE_GENERATOR_H #include "lib/error.hpp" @@ -44,47 +44,45 @@ namespace proc { - namespace play { +namespace node { + + + class DummyImageGenerator + { + + unsigned char buf_[320 * 240 * 3]; ///< working buffer for next frame + + unsigned char outFrame_A_[320 * 240 * 4]; ///< output frame 1 + unsigned char outFrame_B_[320 * 240 * 4]; ///< output frame 2 + + uint current_; + uint frame_; + uint fps_; + + + public: + DummyImageGenerator(uint fps); + + ~DummyImageGenerator() { } + + /** generate the next frame and + * occupy the alternate buffer. + * @return the buffer containing the new frame + */ + LumieraDisplayFrame next(); + + /** just re-return a pointer to the current frame + * without generating any new image data */ + LumieraDisplayFrame current(); + + + private: + + }; - - class DummyImageGenerator - { - - unsigned char buf_[320 * 240 * 3]; ///< working buffer for next frame - - unsigned char outFrame_A_[320 * 240 * 4]; ///< output frame 1 - unsigned char outFrame_B_[320 * 240 * 4]; ///< output frame 2 - - uint current_; - uint frame_; - uint fps_; - - - public: - DummyImageGenerator(uint fps); - - ~DummyImageGenerator() { } - - /** generate the next frame and - * occupy the alternate buffer. - * @return the buffer containing the new frame - */ - LumieraDisplayFrame next(); - - /** just re-return a pointer to the current frame - * without generating any new image data */ - LumieraDisplayFrame current(); - - - private: - - }; - - - - - } // namespace play - -} // namespace proc -#endif // PROC_PLAY_DUMMY_IMAGE_GENERATOR_H + + + +}} // namespace proc::node +#endif diff --git a/src/proc/engine/worker/tick-service.hpp b/src/proc/engine/worker/tick-service.hpp index a67116020..3f93dee56 100644 --- a/src/proc/engine/worker/tick-service.hpp +++ b/src/proc/engine/worker/tick-service.hpp @@ -46,82 +46,80 @@ namespace proc { - namespace play { +namespace node { - using std::tr1::function; - - - - /************************************************************ - * Tick generating service for a periodic callback, - * with adjustable frequency. Quick'n dirty implementation! - */ - class TickService - : backend::ThreadJoinable - { - typedef function Tick; - volatile uint timespan_; - - /** poll interval for new settings in wait state */ - static const uint POLL_TIMEOUT = 1000; - - public: - TickService (Tick callback) - : ThreadJoinable("Tick generator (dummy)" - , bind (&TickService::timerLoop, this, callback) - ) - { - INFO (proc, "TickService started."); - } - - ~TickService () - { - timespan_ = 0; - this->join(); - usleep (200000); // additional delay allowing GTK to dispatch the last output - - INFO (proc, "TickService shutdown."); - } - - - /** set the periodic timer to run with a given frequency, - * starting \em now. Well, not actually now, but at the next - * opportunity. It should be \em now, but this implementation - * is sloppy! setting fps==0 halts (pauses) the timer. - */ - void activate (uint fps) - { - REQUIRE ( 0==fps - ||( 1000000/fps < std::numeric_limits::max() - && 1000000/fps > POLL_TIMEOUT)); - if (fps) - timespan_ = 1000000/fps; // microseconds per tick - else - timespan_ = POLL_TIMEOUT; - } - - - private: - void timerLoop(Tick periodicFun) - { - timespan_ = POLL_TIMEOUT; - while (0 < timespan_) - { - if (timespan_ > POLL_TIMEOUT) - periodicFun(); - - usleep (timespan_); - } - TRACE (proc_dbg, "Tick Thread timer loop exiting..."); - } + using std::tr1::function; - }; - - - - - } // namespace play + + + /************************************************************ + * Tick generating service for a periodic callback, + * with adjustable frequency. Quick'n dirty implementation! + */ + class TickService + : backend::ThreadJoinable + { + typedef function Tick; + volatile uint timespan_; + + /** poll interval for new settings in wait state */ + static const uint POLL_TIMEOUT = 1000; + + public: + TickService (Tick callback) + : ThreadJoinable("Tick generator (dummy)" + , bind (&TickService::timerLoop, this, callback) + ) + { + INFO (proc, "TickService started."); + } + + ~TickService () + { + timespan_ = 0; + this->join(); + usleep (200000); // additional delay allowing GTK to dispatch the last output + + INFO (proc, "TickService shutdown."); + } + + + /** set the periodic timer to run with a given frequency, + * starting \em now. Well, not actually now, but at the next + * opportunity. It should be \em now, but this implementation + * is sloppy! setting fps==0 halts (pauses) the timer. + */ + void activate (uint fps) + { + REQUIRE ( 0==fps + ||( 1000000/fps < std::numeric_limits::max() + && 1000000/fps > POLL_TIMEOUT)); + if (fps) + timespan_ = 1000000/fps; // microseconds per tick + else + timespan_ = POLL_TIMEOUT; + } + + + private: + void timerLoop(Tick periodicFun) + { + timespan_ = POLL_TIMEOUT; + while (0 < timespan_) + { + if (timespan_ > POLL_TIMEOUT) + periodicFun(); + + usleep (timespan_); + } + TRACE (proc_dbg, "Tick Thread timer loop exiting..."); + } -} // namespace proc -#endif // PROC_PLAY_TICKSERVICE_H + }; + + + + +}} // namespace proc::node +#endif diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp index 7186987df..0f002cb1b 100644 --- a/src/proc/play/dummy-play-connection.hpp +++ b/src/proc/play/dummy-play-connection.hpp @@ -37,33 +37,31 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" // -//#include +#include //#include //#include namespace proc { - namespace play { - +namespace play { + // using std::string; // using lumiera::Subsys; // using lumiera::Display; // using lumiera::DummyPlayer; - - - - /******************************************************************** - */ - class DummyPlayConnection - : boost::noncopyable - { - - }; - - - - - } // namespace play - -} // namespace proc + + + + /******************************************************************** + */ + class DummyPlayConnection + : boost::noncopyable + { + + }; + + + + +}} // namespace proc::play #endif diff --git a/src/proc/play/dummy-player-service.cpp b/src/proc/play/dummy-player-service.cpp index ea443f709..d4905f2d4 100644 --- a/src/proc/play/dummy-player-service.cpp +++ b/src/proc/play/dummy-player-service.cpp @@ -22,8 +22,8 @@ #include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" +#include "proc/engine/worker/dummy-image-generator.hpp" +#include "proc/engine/worker/tick-service.hpp" #include "lib/singleton.hpp" extern "C" { @@ -47,6 +47,7 @@ namespace proc { using std::tr1::bind; + namespace { // hidden local details of the service implementation.... /** details of how the DummyPlayer service can be started diff --git a/src/proc/play/dummy-player-service.hpp b/src/proc/play/dummy-player-service.hpp index 75e0f3d78..5cf8438ac 100644 --- a/src/proc/play/dummy-player-service.hpp +++ b/src/proc/play/dummy-player-service.hpp @@ -52,6 +52,11 @@ namespace proc { + namespace node { + class DummyImageGenerator; + class TickService; + } + namespace play { using std::string; @@ -59,9 +64,9 @@ namespace proc { using lumiera::Display; using lumiera::DummyPlayer; + using proc::node::DummyImageGenerator; + using proc::node::TickService; - class DummyImageGenerator; - class TickService; /******************************************************************** diff --git a/src/proc/play/output-manager.cpp b/src/proc/play/output-manager.cpp index 39887ec80..6ffca994f 100644 --- a/src/proc/play/output-manager.cpp +++ b/src/proc/play/output-manager.cpp @@ -21,10 +21,11 @@ * *****************************************************/ -#include "play/output-manager.hpp" +#include "proc/play/output-manager.hpp" +namespace proc { namespace play { @@ -40,4 +41,4 @@ namespace play { -} // namespace play +}} // namespace proc::play diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index f9d0ea85c..44a259c33 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -36,41 +36,39 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" // -//#include +#include //#include //#include namespace proc { - namespace play { - +namespace play { + // using std::string; // using lumiera::Subsys; // using lumiera::Display; // using lumiera::DummyPlayer; - - - - /****************************************************** - * Actual implementation of the DummyPlayer service. - * Creating an instance of this class automatically - * registers the interface lumieraorg_DummyPlayer with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - */ - class PlayerProcess - : boost::noncopyable - { - - public: - - }; - - - - - } // namespace play - -} // namespace proc + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class PlayerProcess + : boost::noncopyable + { + + public: + + }; + + + + +}} // namespace proc::play #endif diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 1b8cc463e..7ee13c480 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -32,75 +32,32 @@ -namespace proc { - namespace play{ - - using std::string; - using lumiera::Subsys; - using std::auto_ptr; - using boost::scoped_ptr; - using std::tr1::bind; - - - namespace { // hidden local details of the service implementation.... - - /** details of how the DummyPlayer service can be started - * and used as independent "subsystem" within main() */ - class DummyPlayerSubsysDescriptor - : public Subsys - { - operator string () const { return "Dummy-Player"; } - - - bool - shouldStart (lumiera::Option&) - { - return false; // for now the DummyPlayerService only comes "up" as dependency, - } // but doesn't start as a subsystem on it's own. - - bool - start (lumiera::Option&, Subsys::SigTerm terminationHandle) - { - ASSERT (!thePlayer_); - - thePlayer_.reset (new DummyPlayerService (terminationHandle)); - return true; - } - - /** manages the actual (single) instance of the player service impl */ - scoped_ptr thePlayer_; - - - void - triggerShutdown () throw() - { - thePlayer_.reset(0); - // note: shutdown of the DummyPlayerService instance may block - // for a short period, until termination of all tick services - } - - bool - checkRunningState () throw() - { - return (thePlayer_); - } - }; - - lib::Singleton theDummyPlayerDescriptor; - - - - - - /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - - - } // (End) hidden service impl details - - - - - /** */ +namespace proc { +namespace play { + +//using std::string; +//using lumiera::Subsys; +//using std::auto_ptr; +//using boost::scoped_ptr; +//using std::tr1::bind; + namespace { // hidden local details of the service implementation.... + + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + + } // (End) hidden service impl details + + + + + /** */ + + }} // namespace proc::play diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index f2b8e2d4d..03780f134 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -41,37 +41,35 @@ namespace proc { - namespace play { +namespace play { + + using std::string; +//using lumiera::Subsys; +//using lumiera::Display; +//using lumiera::DummyPlayer; + - using std::string; - using lumiera::Subsys; - using lumiera::Display; - using lumiera::DummyPlayer; - - // class DummyImageGenerator; // class TickService; - - - - /****************************************************** - * Interface: Player subsystem. - */ - class PlayService - : boost::noncopyable - { - - public: - PlayService(Subsys::SigTerm terminationHandle); - - ~PlayService() { notifyTermination_(&error_); } - - }; - - - - - } // namespace play - -} // namespace proc + + + + /****************************************************** + * Interface: Player subsystem. + */ + class PlayService + : boost::noncopyable + { + + public: + PlayService(); /////TODO Subsys::SigTerm terminationHandle); + + ~PlayService() { }/////TODO notifyTermination_(&error_); } + + }; + + + + +}} // namespace proc::play #endif diff --git a/tests/lib/meta/config-flags-test.cpp b/tests/lib/meta/config-flags-test.cpp index 572bc3adf..57a5621d1 100644 --- a/tests/lib/meta/config-flags-test.cpp +++ b/tests/lib/meta/config-flags-test.cpp @@ -52,6 +52,7 @@ using ::test::Test; using std::string; using std::cout; +using std::endl; namespace lumiera { diff --git a/tests/lib/meta/generator-combinations-test.cpp b/tests/lib/meta/generator-combinations-test.cpp index be6b322fe..2a55998e2 100644 --- a/tests/lib/meta/generator-combinations-test.cpp +++ b/tests/lib/meta/generator-combinations-test.cpp @@ -54,7 +54,7 @@ namespace test { using boost::str; using boost::format; - format fmt ("-<%u%u>%s"); + format formatted ("-<%u%u>%s"); /** * A Test-Template to be instantiated @@ -63,17 +63,19 @@ namespace test { */ template struct TestCase + : BASE { static string visitAll() { T1 param1; T2 param2; - return str(fmt % param1 % param2 - % BASE::visitAll()); + return str(formatted % param1 % param2 + % BASE::visitAll()); } }; + template<> struct TestCase { static string diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index 3e34eab37..09ab07390 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -131,17 +131,17 @@ namespace test { Pick::Type e1; Pick::Type e2; - Pick::Type E3; - Pick::Type Nil; - Pick::Type Irrelevant; + typedef Pick::Type E3; + typedef Pick::Type Nil; + typedef Pick::Type Irrelevant; CHECK (5 == e0); CHECK (6 == e1); CHECK (7 == e2); - CHECK (is_sameType::value); - CHECK (is_sameType::value); - CHECK (is_sameType::value); + CHECK ((is_sameType::value)); + CHECK ((is_sameType::value)); + CHECK ((is_sameType::value)); } diff --git a/tests/lib/meta/typeseq-manip-test.cpp b/tests/lib/meta/typeseq-manip-test.cpp index 1221c610b..6efffd272 100644 --- a/tests/lib/meta/typeseq-manip-test.cpp +++ b/tests/lib/meta/typeseq-manip-test.cpp @@ -48,6 +48,7 @@ using ::test::Test; using std::string; using std::cout; +using std::endl; namespace lumiera { diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index b233ef114..a89b96e48 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -27,7 +27,7 @@ #include "lib/time/timequant.hpp" #include "lib/time/mutation.hpp" #include "proc/asset/meta/time-grid.hpp" -#include "lib/meta/typelist.hpp" +#include "lib/meta/generator-combinations.hpp" #include "lib/util.hpp" #include @@ -47,7 +47,7 @@ namespace test{ using asset::meta::TimeGrid; using lumiera::typelist::Types; - using lumiera::typelist::InstantiateForEach; + using lumiera::typelist::InstantiateChainedCombinations; namespace { inline string @@ -58,6 +58,24 @@ namespace test{ arg.erase (arg.begin()); return entry; } + + + template + struct TestCase + : BASE + { + void + performTestCases() + { + + } + }; + + struct IterationEnd + { + void performTestCases() { } + }; + } @@ -124,22 +142,17 @@ namespace test{ { } - template - struct TestCase4Target - { - void - performTestCases() - { - - } - }; void verifyMatrix_of_MutationCases () { typedef Types KindsOfTarget; - typedef InstantiateForEach TestMatrix; + typedef Types KindsOfSource; + typedef InstantiateChainedCombinations< KindsOfTarget + , KindsOfSource + , TestCase + , IterationEnd > TestMatrix; TestMatrix().performTestCases(); } From b25d85e1ddcb0d776265f6c30ecab0452c1c0f99 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 May 2011 01:46:26 +0200 Subject: [PATCH 019/296] ProcNode hierarchy cleanup --- src/proc/engine/mask.hpp | 4 +- src/proc/engine/pluginadapter.hpp | 4 +- src/proc/engine/projector.cpp | 34 ---------------- src/proc/engine/projector.hpp | 46 --------------------- src/proc/engine/trafo.cpp | 33 --------------- src/proc/engine/trafo.hpp | 67 ------------------------------- 6 files changed, 4 insertions(+), 184 deletions(-) delete mode 100644 src/proc/engine/projector.cpp delete mode 100644 src/proc/engine/projector.hpp delete mode 100644 src/proc/engine/trafo.cpp delete mode 100644 src/proc/engine/trafo.hpp diff --git a/src/proc/engine/mask.hpp b/src/proc/engine/mask.hpp index d2973550f..b805467a3 100644 --- a/src/proc/engine/mask.hpp +++ b/src/proc/engine/mask.hpp @@ -24,7 +24,7 @@ #ifndef ENGINE_MASK_H #define ENGINE_MASK_H -#include "proc/engine/trafo.hpp" +#include "proc/engine/procnode.hpp" @@ -32,7 +32,7 @@ namespace engine { - class Mask : public Trafo + class Mask : public ProcNode {}; diff --git a/src/proc/engine/pluginadapter.hpp b/src/proc/engine/pluginadapter.hpp index 25dabab66..19ed23f3b 100644 --- a/src/proc/engine/pluginadapter.hpp +++ b/src/proc/engine/pluginadapter.hpp @@ -24,7 +24,7 @@ #ifndef ENGINE_PLUGINADAPTER_H #define ENGINE_PLUGINADAPTER_H -#include "proc/engine/trafo.hpp" +#include "proc/engine/procnode.hpp" @@ -37,7 +37,7 @@ namespace engine * Effects processors are typically defined in a separate library and * will be loaded at runtime using Lumiera's plugin interface. */ - class PluginAdapter : public Trafo + class PluginAdapter : public ProcNode { ///////////// }; diff --git a/src/proc/engine/projector.cpp b/src/proc/engine/projector.cpp deleted file mode 100644 index 3b75702ed..000000000 --- a/src/proc/engine/projector.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - Projector - video ProcNode for scaling and translating image data - - 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/engine/projector.hpp" - -namespace engine - { - - /** */ - - - - -} // namespace engine diff --git a/src/proc/engine/projector.hpp b/src/proc/engine/projector.hpp deleted file mode 100644 index b93552e4d..000000000 --- a/src/proc/engine/projector.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - PROJECTOR.hpp - video ProcNode for scaling and translating image data - - 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 ENGINE_PROJECTOR_H -#define ENGINE_PROJECTOR_H - -#include "proc/engine/trafo.hpp" - - - -namespace engine - { - - - /** - * Special video processing node used to scale and translate image data. - */ - class Projector : public Trafo - { - ////////////TODO adapt ctor - }; - - - -} // namespace engine -#endif diff --git a/src/proc/engine/trafo.cpp b/src/proc/engine/trafo.cpp deleted file mode 100644 index 636e59e4e..000000000 --- a/src/proc/engine/trafo.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - Trafo - transforming processing Node - - 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/engine/trafo.hpp" - -namespace engine - { - - /** */ - - - -} // namespace engine diff --git a/src/proc/engine/trafo.hpp b/src/proc/engine/trafo.hpp deleted file mode 100644 index cb1933619..000000000 --- a/src/proc/engine/trafo.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - TRAFO.hpp - transforming processing Node - - 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 ENGINE_TRAFO_H -#define ENGINE_TRAFO_H - -#include "proc/engine/procnode.hpp" - - - -namespace engine - { - - - /** - * abstraction of the most important kind of Processing node, - * which really works on the media data and transforms input - * into ouput. Subclasses include the (Video) Projector - * for scaling/translating, all sorts of effects (Plugins), - * as well as the low level codecs used to decode the raw - * media at the source end of the render pipeline(s) - */ - class Trafo : public ProcNode - { - protected: - Trafo (WiringDescriptor const& wd) - : ProcNode(wd) - { } - - friend class NodeFactory; - - - - /** do the actual calculations. - * @internal dispatch to implementation. - * Client code should use #render() - * @todo obviously we need a parameter!!! - */ - virtual void process() = 0; - }; - - typedef Trafo* PTrafo; ///< @todo handle ProcNode by pointer or by shared-ptr?? - - - -} // namespace engine -#endif From e497f0a41ecc70df3d9011365cdc785633c035df Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 May 2011 01:57:26 +0200 Subject: [PATCH 020/296] test case generator (Cartesian product): unit test pass --- tests/40components.tests | 269 +++++++++--------- .../lib/meta/generator-combinations-test.cpp | 3 +- tests/lib/meta/typelist-manip-test.cpp | 4 +- 3 files changed, 144 insertions(+), 132 deletions(-) diff --git a/tests/40components.tests b/tests/40components.tests index 340203dbf..4541270c7 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -807,135 +807,135 @@ END TEST "typelist manipulation" TypeListManip_test <-<2>-<3>- -out: List2 :-<5>-<6>-<7>- -out: Added2 :-<3>-<4>-<5>- -out: FilterEven :-<2>-<6>- -out: Append1 :- -out: Append2 :-<11>-<22>- -out: Append3 :-<111>- -out: Append4 :-<222>- -out: Append5 :-<1>-<2>-<3>- -out: Append6 :-<5>-<6>-<7>- -out: Append7 :-<111>-<5>-<6>-<7>- -out: Append8 :-<1>-<2>-<3>-<222>- -out: Append9 :-<1>-<2>-<3>-<5>-<6>-<7>- -out: Overl01 :- -out: Overl02 :- -out: Overl03 :- -out: Overl04 :-<9>-<8>-<3>- -out: Overl05 :-<1>-<9>-<8>- -out: Overl06 :-<1>-<2>-<9>- -out: Overl07 :-<1>-<2>-<3>- -out: Overl08 :-<1>-<2>-<3>- -out: Overl09 :-<1>-<2>-<3>- -out: Overl10 :-<1>-<1>-<2>- -out: Overl11 :-<1>-<2>-<3>- -out: Overl12 :-<1>-<2>-<3>- -out: Overl13 :-<1>-<2>-<3>- -out: Front1 :- -out: Front2 :-<1>- -out: Front3 :-<1>-<2>-<3>- -out: Back1 :-<2>-<3>- -out: Back2 :-<3>- -out: Back3 :- -out: Front4 :-<1>- -out: Back4 :-<2>-<3>- -out: Prefix :-<1>-<2>- -out: ElmL :-<3>- -out: NPrefix :- -out: Types :-<3>- -out: NList :- -out: Types :- -out: LL :-<1>-<2>-<3>-<5>-<6>-<7>- -out: List :-<1>-<2>-<3>-<5>-<6>-<7>- -out: First :-<1>- -out: Tail :-<2>-<3>-<5>-<6>-<7>- -out: Prefix :-<1>-<2>-<3>-<5>-<6>- -out: Last :-<7>- -out: HeadEnd :-<1>-<7>- -out: Prefix1 : -out: \+---<11>-<22>-\+- -out: Prefix2 : -out: \+---<101>-<1>-\+ -out: \+---<101>-<2>-\+ -out: \+---<101>-<3>-\+- -out: Prefix3 : -out: \+---<1>-\+ -out: \+---<2>-\+ -out: \+---<3>-\+- -out: Prefix4 : -out: \+---<111>-<1>-<2>-<3>-\+ -out: \+---<111>-<0>-\+ -out: \+---<111>-<5>-<6>-<7>-\+- -out: Prefix5 : -out: \+---<1>-<2>-<3>-<5>-\+ -out: \+---<1>-<2>-<3>-<6>-\+ -out: \+---<1>-<2>-<3>-<7>-\+- -out: Prefix6 : -out: \+---<1>-<2>-<3>-<1>-<2>-<3>-\+ -out: \+---<1>-<2>-<3>-<0>-\+ -out: \+---<1>-<2>-<3>-<5>-<6>-<7>-\+- -out: Dist1 : -out: \+---<11>-<1>-\+ -out: \+---<11>-<2>-\+ -out: \+---<11>-<3>-\+- -out: Dist2 : -out: \+---<11>-<0>-\+ -out: \+---<22>-<0>-\+ -out: \+---<33>-<0>-\+- -out: Dist3 : -out: \+---<11>-<1>-\+ -out: \+---<11>-<2>-\+ -out: \+---<11>-<3>-\+ -out: \+---<22>-<1>-\+ -out: \+---<22>-<2>-\+ -out: \+---<22>-<3>-\+ -out: \+---<33>-<1>-\+ -out: \+---<33>-<2>-\+ -out: \+---<33>-<3>-\+- -out: Dist4 : -out: \+---<11>-<1>-<2>-<3>-\+ -out: \+---<11>-<5>-<6>-<7>-\+ -out: \+---<22>-<1>-<2>-<3>-\+ -out: \+---<22>-<5>-<6>-<7>-\+ -out: \+---<33>-<1>-<2>-<3>-\+ -out: \+---<33>-<5>-<6>-<7>-\+- -out: Down :-<11>-<10>-<9>-<8>-<7>-<6>-<5>-<4>-<3>-<2>-<1>-<0>- -out: Combi : -out: \+---<1>-<2>-<3>-<·>-\+ -out: \+---<1>-<2>-<2>-<·>-\+ -out: \+---<1>-<2>-<1>-<·>-\+ -out: \+---<1>-<2>-<0>-<·>-\+ -out: \+---<1>-<1>-<3>-<·>-\+ -out: \+---<1>-<1>-<2>-<·>-\+ -out: \+---<1>-<1>-<1>-<·>-\+ -out: \+---<1>-<1>-<0>-<·>-\+ -out: \+---<1>-<0>-<3>-<·>-\+ -out: \+---<1>-<0>-<2>-<·>-\+ -out: \+---<1>-<0>-<1>-<·>-\+ -out: \+---<1>-<0>-<0>-<·>-\+ -out: \+---<0>-<2>-<3>-<·>-\+ -out: \+---<0>-<2>-<2>-<·>-\+ -out: \+---<0>-<2>-<1>-<·>-\+ -out: \+---<0>-<2>-<0>-<·>-\+ -out: \+---<0>-<1>-<3>-<·>-\+ -out: \+---<0>-<1>-<2>-<·>-\+ -out: \+---<0>-<1>-<1>-<·>-\+ -out: \+---<0>-<1>-<0>-<·>-\+ -out: \+---<0>-<0>-<3>-<·>-\+ -out: \+---<0>-<0>-<2>-<·>-\+ -out: \+---<0>-<0>-<1>-<·>-\+ -out: \+---<0>-<0>-<0>-<·>-\+- -out: OnOff : -out: \+---<1>-<2>-<3>-<·>-\+ -out: \+---<1>-<2>-<·>-\+ -out: \+---<1>-<3>-<·>-\+ -out: \+---<1>-<·>-\+ -out: \+---<2>-<3>-<·>-\+ -out: \+---<2>-<·>-\+ -out: \+---<3>-<·>-\+ -out: \+---<·>-\+- +out-lit: List1 :-<1>-<2>-<3>- +out-lit: List2 :-<5>-<6>-<7>- +out-lit: Added2 :-<3>-<4>-<5>- +out-lit: FilterEven :-<2>-<6>- +out-lit: Append1 :- +out-lit: Append2 :-<11>-<22>- +out-lit: Append3 :-<111>- +out-lit: Append4 :-<222>- +out-lit: Append5 :-<1>-<2>-<3>- +out-lit: Append6 :-<5>-<6>-<7>- +out-lit: Append7 :-<111>-<5>-<6>-<7>- +out-lit: Append8 :-<1>-<2>-<3>-<222>- +out-lit: Append9 :-<1>-<2>-<3>-<5>-<6>-<7>- +out-lit: Overl01 :- +out-lit: Overl02 :- +out-lit: Overl03 :- +out-lit: Overl04 :-<9>-<8>-<3>- +out-lit: Overl05 :-<1>-<9>-<8>- +out-lit: Overl06 :-<1>-<2>-<9>- +out-lit: Overl07 :-<1>-<2>-<3>- +out-lit: Overl08 :-<1>-<2>-<3>- +out-lit: Overl09 :-<1>-<2>-<3>- +out-lit: Overl10 :-<1>-<1>-<2>- +out-lit: Overl11 :-<1>-<2>-<3>- +out-lit: Overl12 :-<1>-<2>-<3>- +out-lit: Overl13 :-<1>-<2>-<3>- +out-lit: Front1 :- +out-lit: Front2 :-<1>- +out-lit: Front3 :-<1>-<2>-<3>- +out-lit: Back1 :-<2>-<3>- +out-lit: Back2 :-<3>- +out-lit: Back3 :- +out-lit: Front4 :-<1>- +out-lit: Back4 :-<2>-<3>- +out-lit: Prefix :-<1>-<2>- +out-lit: ElmL :-<3>- +out-lit: NPrefix :- +out-lit: Types :-<3>- +out-lit: NList :- +out-lit: Types :- +out-lit: LL :-<1>-<2>-<3>-<5>-<6>-<7>- +out-lit: List :-<1>-<2>-<3>-<5>-<6>-<7>- +out-lit: First :-<1>- +out-lit: Tail :-<2>-<3>-<5>-<6>-<7>- +out-lit: Prefix :-<1>-<2>-<3>-<5>-<6>- +out-lit: Last :-<7>- +out-lit: HeadEnd :-<1>-<7>- +out-lit: Prefix1 : +out-lit: +---<11>-<22>-+- +out-lit: Prefix2 : +out-lit: +---<101>-<1>-+ +out-lit: +---<101>-<2>-+ +out-lit: +---<101>-<3>-+- +out-lit: Prefix3 : +out-lit: +---<1>-+ +out-lit: +---<2>-+ +out-lit: +---<3>-+- +out-lit: Prefix4 : +out-lit: +---<111>-<1>-<2>-<3>-+ +out-lit: +---<111>-<0>-+ +out-lit: +---<111>-<5>-<6>-<7>-+- +out-lit: Prefix5 : +out-lit: +---<1>-<2>-<3>-<5>-+ +out-lit: +---<1>-<2>-<3>-<6>-+ +out-lit: +---<1>-<2>-<3>-<7>-+- +out-lit: Prefix6 : +out-lit: +---<1>-<2>-<3>-<1>-<2>-<3>-+ +out-lit: +---<1>-<2>-<3>-<0>-+ +out-lit: +---<1>-<2>-<3>-<5>-<6>-<7>-+- +out-lit: Dist1 : +out-lit: +---<11>-<1>-+ +out-lit: +---<11>-<2>-+ +out-lit: +---<11>-<3>-+- +out-lit: Dist2 : +out-lit: +---<11>-<0>-+ +out-lit: +---<22>-<0>-+ +out-lit: +---<33>-<0>-+- +out-lit: Dist3 : +out-lit: +---<11>-<1>-+ +out-lit: +---<11>-<2>-+ +out-lit: +---<11>-<3>-+ +out-lit: +---<22>-<1>-+ +out-lit: +---<22>-<2>-+ +out-lit: +---<22>-<3>-+ +out-lit: +---<33>-<1>-+ +out-lit: +---<33>-<2>-+ +out-lit: +---<33>-<3>-+- +out-lit: Dist4 : +out-lit: +---<11>-<1>-<2>-<3>-+ +out-lit: +---<11>-<5>-<6>-<7>-+ +out-lit: +---<22>-<1>-<2>-<3>-+ +out-lit: +---<22>-<5>-<6>-<7>-+ +out-lit: +---<33>-<1>-<2>-<3>-+ +out-lit: +---<33>-<5>-<6>-<7>-+- +out-lit: Down :-<11>-<10>-<9>-<8>-<7>-<6>-<5>-<4>-<3>-<2>-<1>-<0>- +out-lit: Combi : +out-lit: +---<1>-<2>-<3>-<·>-+ +out-lit: +---<1>-<2>-<2>-<·>-+ +out-lit: +---<1>-<2>-<1>-<·>-+ +out-lit: +---<1>-<2>-<0>-<·>-+ +out-lit: +---<1>-<1>-<3>-<·>-+ +out-lit: +---<1>-<1>-<2>-<·>-+ +out-lit: +---<1>-<1>-<1>-<·>-+ +out-lit: +---<1>-<1>-<0>-<·>-+ +out-lit: +---<1>-<0>-<3>-<·>-+ +out-lit: +---<1>-<0>-<2>-<·>-+ +out-lit: +---<1>-<0>-<1>-<·>-+ +out-lit: +---<1>-<0>-<0>-<·>-+ +out-lit: +---<0>-<2>-<3>-<·>-+ +out-lit: +---<0>-<2>-<2>-<·>-+ +out-lit: +---<0>-<2>-<1>-<·>-+ +out-lit: +---<0>-<2>-<0>-<·>-+ +out-lit: +---<0>-<1>-<3>-<·>-+ +out-lit: +---<0>-<1>-<2>-<·>-+ +out-lit: +---<0>-<1>-<1>-<·>-+ +out-lit: +---<0>-<1>-<0>-<·>-+ +out-lit: +---<0>-<0>-<3>-<·>-+ +out-lit: +---<0>-<0>-<2>-<·>-+ +out-lit: +---<0>-<0>-<1>-<·>-+ +out-lit: +---<0>-<0>-<0>-<·>-+- +out-lit: OnOff : +out-lit: +---<1>-<2>-<3>-<·>-+ +out-lit: +---<1>-<2>-<·>-+ +out-lit: +---<1>-<3>-<·>-+ +out-lit: +---<1>-<·>-+ +out-lit: +---<2>-<3>-<·>-+ +out-lit: +---<2>-<·>-+ +out-lit: +---<3>-<·>-+ +out-lit: +---<·>-+- return: 0 END @@ -1105,6 +1105,17 @@ END TEST "Typelist combinations generator" GeneratorCombinations_test <-<2>-+ +out-lit: +---<1>-<4>-+ +out-lit: +---<1>-<6>-+ +out-lit: +---<3>-<2>-+ +out-lit: +---<3>-<4>-+ +out-lit: +---<3>-<6>-+ +out-lit: +---<5>-<2>-+ +out-lit: +---<5>-<4>-+ +out-lit: +---<5>-<6>-+- +out-lit: All-Test-Combinations--<12>-<14>-<16>-<32>-<34>-<36>-<52>-<54>-<56>-| return: 0 END diff --git a/tests/lib/meta/generator-combinations-test.cpp b/tests/lib/meta/generator-combinations-test.cpp index 2a55998e2..4c90b1f93 100644 --- a/tests/lib/meta/generator-combinations-test.cpp +++ b/tests/lib/meta/generator-combinations-test.cpp @@ -70,7 +70,8 @@ namespace test { { T1 param1; T2 param2; - return str(formatted % param1 % param2 + return str(formatted % uint(param1) + % uint(param2) % BASE::visitAll()); } }; diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index 09ab07390..a14daef90 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -139,8 +139,8 @@ namespace test { CHECK (6 == e1); CHECK (7 == e2); - CHECK ((is_sameType::value)); - CHECK ((is_sameType::value)); + CHECK ((is_sameType ::value)); + CHECK ((is_sameType ::value)); CHECK ((is_sameType::value)); } From 5e8a9b50d131e4dd934788755ddfed143facb77b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 May 2011 02:22:57 +0200 Subject: [PATCH 021/296] define asset::Viewer --- doc/devel/uml/fig130309.png | Bin 37738 -> 38015 bytes doc/devel/uml/fig130437.png | Bin 32728 -> 33013 bytes doc/devel/uml/fig131205.png | Bin 12187 -> 13639 bytes src/proc/asset/viewer.cpp | 34 ++++++++++++------------- src/proc/asset/viewer.hpp | 35 ++++++++++++++++---------- uml/lumiera/128133 | 24 +++++++++++++++++- uml/lumiera/130309.diagram | 23 ++++++++++++----- uml/lumiera/131205.diagram | 48 +++++++++++++++++++++++++++--------- uml/lumiera/5.session | 12 ++++++--- wiki/renderengine.html | 5 ++-- 10 files changed, 126 insertions(+), 55 deletions(-) diff --git a/doc/devel/uml/fig130309.png b/doc/devel/uml/fig130309.png index d63c8432b93d39c2154ee10fb143e3e62549a70e..6afb6d27999d2bd1b9990ffdf973dae1a34c1587 100644 GIT binary patch literal 38015 zcmd?RbyU@D*9LeL1(lK#kP?uPmJn$a5S0=sDJe+_=?;q&5Rp!ilI{+rySux)o7uQYnAdJzLm&{CPacatM<7r{5QvK> zS1!VD(w`irArQ9^PedQc+eNO7x~j?Po(pa4KMaW>)I8D9~ZoDRZ513CWg6~p^Fh6pP8@q znYwenG3MLeb=p}lw=s`;y`H;l?C2O}sjl;io%jMQ0f9zrz=ym=EZlx&fxK1wD2W9( zZmFLekxzI-z|MpGb;d(lG`RVx&@h9%TXZdq6nU#e8HfTm6Ab@dBnx-!^sh5O#mX!u%g^T?8KwSMMU#=y+|=aq%2uDKu%Tg9 zl+#jA&G|HHXNS72ZCx_5sHbytbzy;%ih(urxL#-s@5(#<7SD3mbKBWRSY*7`GY!f= z^2Er$SX^Crwxk6(qF==%$R_yOPJgtWrbATt?wy3_*WkU~q5POh(Tj+QORp@VWmC(SZR`^a)rHR z-}$ktYo7AOp@gPJ4(p}RT>Zk@nuE)iX>epUHK&?_&h*-*JVCQrJJH); z8<73(F7QJ%Ah7#73d;N7;B(>@dWMuYHZC5c_t=k8srdyiZ ziX3g=;J{9E%O^`KQh6m?M(#qcfJ{b8!U)$CnO^wM$FORIwM-&b=^ri|W#gkSz9ja(+H#7viA7;8>BD*=mW1lmV-(NR76KQL4^P#AX zP2b@mp#c9S_+LTZ_`^#4gQTR!78Wdaj_5McC+o%EoE~KhJ@^%E zO-`p}zH5V&Ys{+6ft-pXmq@uXc^*EUvqGVB^-n zIbML%`+D^IHcXp)tBzV)jn99`GnhPid?g%KUFKJ&=X8>>vQl*cg=uvq4gDHRYv|p{ zUL|o>^^%XCOiV3%>;9=}U-OJ^bM2;{ocKtDoLP?C=i8G|DV}MpUoOyVy=g;|`Z!r4 zLSOcnuDv0^r>a^%hQ~6@v#X1WlngECYY&rhPMB0#fr)CHp6s`8ad>nt8yiHW_LhpN zmY+uHnbR?^J^k`DVY+V6r&gosm3@=Gy!_Wpr%bqP+=ct5V<&}PPxhrFAK!OHAUNUL zIS#eY%*1yEn~q`(v37L>^| zKau};i-q`E|fv9a+p%FUf3tOv5Q)|G-=>iKN(a-sRPd&0%$_l52(IzEI`a(fQ|b6a>HA z=SD;MCr?`EW9FUqlsR$K%a1s42=LWz3OJbilgl0NpX+N+o!ygwqc}1$wmjHvx!Bce z%x7wSDhg+{WIQ-8}&%?rx6e4K$ z*90~;l@0ageobGVnG3$&*%D?hdK;fbD}s#6Xd#Rv1OkUbz=23L$8o<<#G^vj_{6Bv zMeyp?FknB6q; zwDWt+Z93P(?eRQ8jK({jLI5Orkk!Pr**Yaktbf1sv=y1N2O15ur zb#ierK)+ovPkC#zK1NCL>C>m|Wo328M|Bbj%*x7}qVg|7TkbL9^WL}-93`MCsP&Fo z)ncY$Fe@vJfS{C$dcLl9^(z{i#f)Zv!4pNr3Q-HyV(B*CrMv zjPs4}YFisx)BPGT0!{~-W}%2y*q?(N2%xu+h+Pv9Mqb3DPcPp%bRSM{?aCtz%TO z(J0yZAwOhsu(61SeRFO6{PbWm-gex1dfH!zj%pT{B;pKShTHh-JzBrh!DL*B##;3~ zyX`r3o9f!yv#O6N+w-YvYAb_Z5+n*+A{Ps^s!OZgF`s>Ut3`B37Tjd*j!BYb*iAU@ z>Z-dsq;A|87=6=_6ye4qOq)a*!L2`9dG7eEV;Kq!$^ERDeCtYxJR2+0ox}Nh@lP-4 zZ@3=T624|;YWXSw73AZ@1aW31d9L&EWVVjI@$0#{L@%$fApxp|4{()GF|s202M7E1 z2*m3jKxSMWbu3fLoR4k@|tm2H>x*fiZ{ zV;`^{9-i)BbBqzZaqr%#hRdOtMDv(&<5ca-^~%cCR_OM#2{~z%;;_=9s-f<9nkn{$nSyeT@32LSwp^Sty)BdT(A4GhEtU?`;3^5-mB`% z+7%77nZ{7HfUwepxh_x>gyohj}+%{24AHuoG;4n+jtX`@u zFEfom2r~jM|Eb~RoOEy`-Dv~qEtX+q3)xNl3z|;~7FYg4g3t^1vGs8v58?ngeJ`>s z&M$DARDFh+$LGx%0%1U&k%4^q{{r-RV|z`6ix?qCAo9?|)X?~yLcgzGhkJ;#cg3k> zn%eB?@wchz>AkcJb`~awu#gQxv~|M_?_88^pXITjqxci}r`<2e*Cd4%jm+W9{pu}oi#k=9NVnNbA)B!Uq82+uN95F0I6ynEhFpvcDR^>_rBeCd4`~;s<^oC28;>{G9TYAOiw>%SX-XSOHL5$>*-mUB=SkRto4u4alGYZEb90`Q#VP4M4o51+Urz`xcep_}&s>-h23c0_of<%3%j02TMQBZjQCU{!6qB?nV5sOKA@l};}6 z%`U3KX)%3(e9#0dUBEXy{0LHCOKS`X`;6{x0qjepn`8zGQ5`cw(|&5Ebmh^~Lb~d> zgp5!Q9O$sH#?#}<%Dtfj8A0WiJ}L1G!(ylc^3us#n7O#Oz6bt z(4rXiSqbHc*X~HtfS=}-1zCrYd1g`3p}G0?a3=NmvzHtg2;smPLd@tLbSlJ8v4eaT zL=-iyZet-H?jG??>9g4oM!2*YR2}zMWKtS$luy3iQsm{yGCX^adcmiwl>_?e)Vdg zqhn!RsI{dAPuc8my-m{ds)0z;=BT zN}C6kbLdyAiLr7tfQhUxCI*)#!?|Meb1f!!mu;DtnLQi2x*k#$kLn`RS@?TNEMd!) zRkXReV96N4jM&&u%=TR>@7cpv4mQzs>M(>XX7H#=tQJ!W_nV-63OW_B(a? z&Y36a%BuUb+a{?gw=Z7E%y~|AeIsuRm^C*CAfY-Up%GhHu$N2nYi!}a>vVuX?pjd) z%`yvJZk*NO!l&6;@weZ5s;QAad^q-u$!vGyNjgqI%y>^^B>Ke*taS5bG2`-Fq}ULc zBzpV^13wRsTlemj+nQyjtDZ-NA62-=IJZSM*4Fl7V)_G3pxy1DpE6KR!O+meAo($E zATbeJW22U|w1}st)(92V+}Gf~d-v`IR8eWE_z_H89k?u&$HKv3*&5E(+uJ*YbqV3N z{l;2B0gf1>(oqZ#wV&6fYMI^pGBtYA%?&M`RIw^VEE#}37FO1NBse%ighi%64I2AIcN9Fp1CEFf^X$>+lbVZIXUWjzB=bVx-;Zj#apf3($J^g`!x<)^~-#o5_7rH_el z8VRo=t7-@0B=>~^V0ejOV?Y1#S~kf?kG@XDeEpwCcX@t436b_^Fn4$R%ggr+ z4r2D$h{B(qMTV83<34!|4~L)n`_Eb9kDd=2r=>g~0CNb`E4y zLhq=k>iAx31R62;SLcT*I~L$sGsWucM2j0xSon;ot#4@Z0>TXyd2*851^FOsY{SCL zp-yMh{FhZSdOcXx|B_^X)K+L_Hpq{xnGcwV8m}p04z;hgx8ousw;?#_pcLJM<8#9g zS`JPuC8Z0T3__JV03hsl7P6hfbN|f8N8`Ha`mH68L|?v~7K=*@zA;#0y}mMJElK-5 zIk~ur54ZDg!I^$20z2PY4ae(|^U=n!2a;*~fM+(l4@bLc}|vKFcS z4(y+D?noXPvdUkC9^LMMO?;Tr?TmwSCHV7odF4BRzy><9A^KOm5sHWFiS$WXkn9SYW=hKL((UPF4T0@POgt-4pCR|;d0Ix>Q zS?j9gbedm?=(>(?ir*h$B8$8U8f?qWcX0MyT;#Ax?>`ZZadqKm>tLj(ze7oxoW;gN zP0h~6Ci_x~o>a@&I5hmG?6WK`R+*ljo|a(7OuRay{M$ru&fS*SrXMJrpCm1RxGDi0 z&Uoc{>G$c_xVUTj^R@#*jfAMwR5CKZzKKxK(vSfm_yC9q^c!|LvrWq%vQ?O`LQ+7) z^1iZ2Vdvm5w<5(Si@N7p!H4tL;)`U`UbYSlENo0xef|1IuA7{TYuCx8h)wQPZ3>saY1t*4(edMlG9273cZ3-rKN0aTvC7np~Uh5iU@X4NdIWrVZ^fqyXm@20s`ybO+m>uWF^M0 zE)o-Mxx<?kO-JUlIph7Ea!I9^_q)Z$ozc5H=NS;m7o+#(`k>EV9`)W1ve7N9=ieqm(Jfc_ic zaD2RNRHVDSk_PsbgQ8>uzT>2i=t&+P?FD%|85w8D?(k^eH8(zse3&W~N4^j-lZL(cs`4xQ!;5WN=~(NY^0mZ~`&<51f5!!;s5} zgB##P^Kdo+Gbm!|cR>yCym|9xYpYc0ktZ~Os>p13c{NRZ;AsRS&e>|0JM;hwL87-N z6cnFcy7*G*H)=)))ak7cYc@NgD$Hr%x|@7q6$UA`C;J6=>sSUVk&Jb^wwsU$ba zgM$0o!egaz7drw{DyJkdhUURY)o01Rwvas3%uh17p3U^`U9;I)IDE{8hB$Ef+8XYM2bkA1G-Q4I);aE~ z@?t>Jgq(bV?2z*Xys-`uRL4Jip)84q4Zx$53mU5xcb&Hon@fHgpQb&3{(;2=J73}PTY+*x<)=SKMsTSZ=;`H{xbNM2{&LgDlgPao$U#EZ zmqGpf_BkIs{U|Cm_4TDhJ+KKA<3G5b?p6w(-QP$r6zu6SPxd~-%G1v+7vP{jfV}um>{IYLs(WIEU)~<*471?e=0e;~VBow70Qy|HYX4nA2bpqC z-BVLjwl=QU)Ftk(t>#-VR}P=sC)^X*SM$&Cytec5Z}E*#|8SQ_;p%Ucv%IrbdF~?V zoc%L5uB>cru66vw!+KDPAl^fXApxZ$&kel|X4Tdf?~lA$er>&_JEGT486VO(!cuM^6GZs4`*GRXf6crB(OH^W5OH^P-0^T?%pw9UU%tkH zmM#N{f&UY69fOBtdz5?xC9Rjy{~l^4mmoNKfA_X}#>TI7y4SlBWOQ@NfIQ2^Gk}$3 z>B|HHP^B_5FyQ3o?p6E=XFm6Lbn)3fLPInA`ZVFWp~S<7*9EkGJro=-NO=m769VDl z1({SzSOyESvy@$tM}^0FZ+MW+Ss z^P3PM_HfoD0;jisJUeHJ+APb__B*r_FJE?6RLDIP5%ZSt$#=;3Ay;2^yq4&JO>(^J z=(4}|0v(;hr2I;(y@-d3A>SnkWA)I|loU@-lOI2DE?-vN-Vy~8-+1!mklfxrZgm(N z@1G*tt#Mg;(qDcx^oizaN-wk1GAkk7_81vvE4C95g%Xi0+pej3LvDte+Dgg_CuTwL zQ{#`NKSFk-bU#)cd-^gu|6mmkH7YR?QNt^Cq|T+&ZxzMaXxNwbdW#HVU&cs|s@O!g zHz_$euD44&J^JV2Nv@7BS2e? zf#P1Z8iMC#Jf@EGq`h>!@CU7~K#tn9>zjV)MjQM;UVwU!@USr86!GvxzB0xqiL)r@ z@CdpPR|f~ymtpbmlD;PK!>rihZLf3 z`nWjQu=7L;m3V1wgki^;u}vd)#x3=YHG!}t+jHRLmOcWKgr7g9?0ftQNb!eEAgPa-<8=t%=8X2B4#s2z6j(!s53AmqP(cUd0P8 zD*ABkqXaZ(fN0HCv#EmW(O;BUap0)`m8okIiW(~~m32C)8ZE6%PcNTbBfo>sVs5Rj zsJKUsZ2if&Szb-mBzzZVz2TJuz0=vI|Nf*MF@xoAT-SqdO97j6N`fkzZt zWRY+Vr6`oBvet*Yp?SU~O#WGx^>9J8aTN5UNzx|fmX=1j0e_zAuK;adghmG75EF$y z7RA9CelAB1vh>`n7xalJD0n(Lr)^OZn>(bc^77xpP>emC-e4U9zvt%n)7gSd?&Zt+ zIsHIdp4M<{sh4XroHajx0?q%?)7$4Yt&{~Vl~O;B$vvuok%F8hxj)!HRUk4Ri~f@l zD0bMDF8KKQGk3{B7O$|&cI1+68|C(U{R?X}ml5g0wEcJgb=)yA&Tfw86;gyosIogB z6}@}MK6~RPNyOBYAm)`fLT@Hs>FGrR?ky=%EiR@JwK{mC_4H}c4@3X(a0fvlC}V<- z1x{Ntvf7&=?Lua2%X|sT{xS+%eDm@rdsQK%3*0A`ml1iF!_?~kapq0>2D7Od?%Pe5 zSUX<2NX|DiRCcd3_b zFuNS@he(L_g(6QzIv%B|ONd@jW=;WrqZ87$M3=kQ=SoXCYYa~sJQQa|o)R||&9#)H zKkBCA&Vt>OC+lKE=2=F)*zyxVv!P&lb`jenlY;ueDY-f{qyP5 zCqRrRr>DSN;Zr!++kbR-KijOQKoXJJ1D&0n!^07tOUujYX=!=RoCw55-si>#AV_DZfu59Es%)$E@X zymTsKj0oc4xH_BSOXpFae3 zf}0Xv6^)ybk%A-;-o5+c*-%?cNvRbh8LXu0NX;p~Q%`}7iGfiw#Rkm6*cioiS?5zS zFdD7A1OTm+d1h>EEGoLVC|+RFe2;^p*!6rL2$S4nT7D2NBbi7NdKeV`-NyP}R4P;wF_u)woP5Yfib-E-U4WdbPQDgUg z8(DurXQt=RtGtr`7mEFlCQ%WV2-L)X*?5^TJJFhglM`m>SzI<^DQRXIshS#j&VQku ziPp>yKN3qYI7*Ox%TQP|`O?@e!`+q`QJB0X!wP*o14~92XJeQrbhKGCVqyefp~@P5 z^vI;_OZr@SNzr{zQDr#poa{@92U<&ucS5<>tM5fX?g8DoqpK@!pzWVj!9SRx(>#w9 zngd_JL2iBzX_FWnkKaPZ?N0Is=0Zop*!W0Sua1WoD$ zm5Y$NqeC+P_gsA)aK5Sn4ZLYk5O7aPXYc!&n5Y3t#8Npa7l@ndP8=Ml17DgbO%@nt zuic%<+SFuY#FclR4aE2Be$#ZdMGA__*9*@WHOB8my%tAdM6tCJubLorJDQ+)j2MY) za=elHIk~A?jSZQH@U;6nU8T-Va$C8$iP>Gw9pMg0&imO>_crw}7X^^L-0J5?H z9>mDgTNCOPCt(HO4I;j26@K^-+w`>YX!jR%$a!Rb;gi~o0J{Tk1$N{8#=##lK8B@3 zcr^iMrmv|74+=9D$za__twffdTON{F_pfpwDA-1PC-dAI=N zD_5wiluSdzBk`2ozN6E}oQM9HMRfx5&3We4X^*k6pMy z%ukHd_{x686_55lslbt!Wbl3~_wsKr9rX28!z+7mmDTypX7|pN(ytA7Y?uJI*0k%j zu$*~L6F94@TboNGW3=r-(X}?hW2~upD{vOT@yhu5*)!Va>D$pJvOnrEoFp(Mj|e_&=t?en^X z=-C+s4J|4iT_qtxk*n%M5uANI61>3A_Qj{zc&xKW=f|1L5*~Xtl zzdcy=QQPj8H!<7KuC_LZs!JXrCtajqc>gR!bX)`{T`|a7n(vpgz;v-R|3^j9rIM&v_etyA~?pzg@gHLlr)!41BMDGdK$6S%Kwz+r)$>I6Z@&;^E?Y8r1ud{rdH5xi1~>%B2;0GN1Zs zUkahImX@^QVxGHq13ZmQOlWYu03(*QjTBqTJ%8@x{j%6XY%}xu4}d~%)<%kHX=#_P z;Ue(0))*F00v_+|_-2KCfC{|h&On#zO;-l%2lNHoRA@R1A(rBRp;}=4wsuvVT)^^ZBN~>y=CIh&l8|DqV#H1V#r3?WbzI{QUe@1bVh; z(u`ieKDWV118>1qJldk7qAMS(;A-vNQDJrp3J))VSi)#~OKzZlK@2Y6(S`q*iPY!= z8`(KzE}=S*-G8(7gpWlddPEw%=^se7MEY{7&_xs!9UYzaD8Bi{MP+$;d5HPoV8v(8 z-fYb@?k@M^$=cZ51A~n6Wf1#s-^RajL+{nAOQ@(h$B)FtV|cBbfIc4^Y6@a(kL1C_ z!^^eX)IQ9GMdHy(uZ)(4cnTh`y!AbXVml@J@ajD9923buW4q=$w|M(|oo{^4lGCU0S8Vp1(wY3+E{ zN#V-R$;Niwry9(@H~}wC68zgmskZiRc-IbUZ*rVcyF(8lSBv*Mf{z;%-YdFn#Ve7}tp~%4|7uG4&>-Xb(pZMSr3CCzkX6R5+ z6(l4+!pApmZN>w#2(L3*fXC-Xc^ylhs)BWt=f%Hs4gvqM8fmF+Y)3TtjX9qkZvGU` zgcJ>cr>bh^)2D`?KSc|q|IW*jprwe|@sADW>U%wAx}mS9cXWIV-4HDuU9MM@_B&l2 z9q_`^gh|@04sq=z8r$G7QGH>3r>n0Y5)&gMdU$vUE#j8`T@b(7*fa?%REpkw(?$Tq z?LgVGBr+s*i~glrB9N}f&-Q89cHCqne|ezX5!+#h?Pb=_?$IbST`oBnkC7CG3!9th zQc|PqW2a3u@ugFK#(WsldlSF7@o1TN%(bV5&Wo3NelWKS%y)Kdc&5V{IXKv~740cM z?iqJIuWM-7*qly`7D(d0N8aDFxit03V`^$DK_Ub<;N!>Iv(sa4lObqz1vGym`1nfS zy+g)>sF>KMU71e3-)Mz19}|;g#g)F9#z3%ujC)~_q}_r-Ik6D6sAAf z$l1BztoE+o!9+3e0oa5dM(~F75Gs7Cs zU*!%0wtL3*4h}V4395?eM?F4cc#ebpJor6`|T`qGVGU!umGMRp8;ndoRmg zL`0I_AmFkcE?CKbZF{HxKj!t;dervV5w%W2jWjOKm4a7|dbN=3?zKg5({Rei*A5_r zeW5K${`FnlKR0K~cHM7xJL2f<*fJ+)>d`{;h2O3WVY3@15t6~nTX8z&!!b2&3&lh?VPqfhm5mD1~b2DK7qnSMgYo~z2PAKzA|N7zC zsm4NgeVpt0d_D;|A{i-Xaur!GbB9UJO=wmN?rhy$UuEXEm@$4a(Bkc_9uQzTJ!OL{ ztEkA21H(VAC!6y@L0`Un!C=kYtf<%o8Rzd_@c=QOK5X)nu42nOz&OmeD0h!A={B2< zS5RDMGnuSf{Ph|dtOnCX5Bf1l2V}tPrNlbFd2wZBF7rh|N;dH( zqN5i$a$(G)UB_sx(50oL0BSV6PCqj7i12uF@^{Y16~}WC>t4ydCYC}c&(xLg5Z`O@ zcLss{pN{V@o-l!h59NT=7!@lLsyg{$vMNxhAm8Zd$gPhFoS&}CX}CH+s=%cbX0*00 z1jp5Ip{@ETGu4;Ityk>IhKA|F*GU1$7av*W=-#~8LLTB|sE4Ml!(}9w4+PE)C zWyXtu5+jA5m1p_fz?g7zC*|d3`jaz%QL3=t&hV8w2ZibMcp7>cu!mQkSj1>mqZ$wG z)aK}#k@FvnCwP&Op_zf zncoLi{~;dUv{v;AyaUYSlv;BPR5%|NTPnej+l-Pd2Rqdu#mDA07Jj=ozFQ8QW|gb< zU{ru3Ft~RCUn(3QX_){9Bl09#dvd0+S?I_>#=?RKbq2A60#W?<&*m8zF9wFCl@(MY zKB@&bLX8#cjftDJU!)>}jfe6+jJ_=0GqIUo>UCbJgK8z878w*&sa?wtl*G7;qcqdm zRzk>2Hg4C9ti}J3 zHxmBQQD<9^IhrjWY_9te=N^DJe^2%Aw#DxrXhU`L!!BEmWAibt&b8h>;4VM2DT^;D z85?tX8`%9B1U>`WhoIvqb-&z?W;Duu>1B{dW6k`ogJ)BmMyHO46?)D!rbTo@Y> z@sI{UcDf;=!j}%F-17+(SEMfwc#J%&*jO^CSiGE3Z#E`Ej9%}9yRSCW4LaOr;FnU8 zldC}4wTUR7jhy|j$^rI0(YUm+a>tv*)`8>C`2W&cyS)B;%N#<1`z9yNOgvxl#~>hkbgVu5M*v3jQ_QG$*5E?vRh9ZiId%2)Rbillz+@ho z0*2`Bq(D`Irulc-Bw(eTo|^Cl6hmjSi!H`0Cm*3x|gy8`&3qt@j|8C=udf6wEqZ>2n0P$gmu^xCO8}&)O1VS6 zdrW{9(TE$;zOnjqM1ia}(Hx`NfbTlo?@(`;u?leF#A-x(-SbewBHIkDWeI-Y87eeyq>a%~nL(>}tl zTE^}7?-05$lM8R~-${!8_1NY~_PfB`(IszP_WqabX+?PBGmwLBd;c1PmbTTZphGSc z`(K07CFqt)u&`L?f61oZAe$o>u==m*xyZGu5#;!=0R?1q`1yhI$T< z{AG?Ksg%tYNh%Hj)d)9ksTlcSruHTKtV4vlO}^viWJgf`!?mN>0Y$x$a)C(2lqDutBf&W zC5wjKZsioGw^M>VWHiTt;0fc>JjN;i5%SmfhgJL@9e1Oh8259)h)U(|2qJyg_T^un zm(-0`bFPeBWPS0cjjLo4lVX-s9r+-pYP7|?+J8+i;2pMcmG)36#$8Mr7|i+OdjbB}o%w&bBoPxdIBk&?;}{ z0AX=!+3~v=-(77lF5WN0Ji-?=A^l+tmvig*`E1F)*!52eewP(zOGg^KegGqvA7577W{R&y! zQ4*trC|o^ZI%KgQEj>Tn?LW6&9g^zZXXoa2UQYj^PDqffWSX%6!)|TS0){Y7$?X1% zt;L&w5qi?D1PQO?ygaL_4_8@OSRQ`M%FFx8Ow4DqQe-~;P()-<9mtE((b10&zon+W zR#qkk55TKWB;2OJZvZ;J;U%iVPugm?5vw&>^#P;|9v+g&D36NWK3=Ml--Dui(s_N0 zWZ&@Q53R)f8z2)b#KTl~G>_#Wm_9ncdu0D>o>Lags?_2vIazkAqqf}H)@R!>?m$A~ zb*_z$W04&NXK$vS9v$Akz0%z$D||dM5()n(_ibALh@#}E$3S9(9;hTn2_LHMn%nliKUh>j=Oj7`Vg|x z#@UY9)A90_BqujiSHDlE08#hz=Vvfn9}s{;M1*uE-~ej~e2TrjJ=D7;Oc^v_gzs@n zfQuB+`9Pi_Oas35G=TPv3inzFy-mW@V;;VX@gqT7Wck+j+wm0y)n^6XwLgczjJo}| z!Os6>1QZ6Is@;{z_<{|zyUKgl#Qh>M(Ib>)v!Rljs*OnddSElai4v)9ba9~&6K@YB z9IJ2^x}`Z3C$@}+?Xoyhj875cWjU7yiog2@W2aN=kU%a8AQ6`1S;^3mY3fB0V7GfXNiK0#hJJ z-u(c&C#yh!u|e*_?&`2(e?}$yo7u*9EGhNslEEkg~K~j6afauYq~pbTG%b zC3F(VeQs0!X)dx#oVZe#Cd}nI0yAj;OJw2J=_#B?9%`8l* zwxMw(37+B%U|D^7pqk=T(!@BOi;oQ3;-UeKqG!G7s@#Nxjs@U1bk))6K07rBX;eX> zU&E!?|NVO@n4I3&81%rJEgW(N^U{+i>m|iJ2?@^5FjrMqjTSU8R1v3UfV8pzyK&E~ zO(k1<42FO-?0%KGo)fxb*uy}wKX@~lUucXtSrGA;#0J91zF70Hdm ziB4B7G=%|Ox)-QQ1*bV>iH72^u~h-!e?)7notQvR3XbrA~r6 zH)OOAmj5x>-Uze0-4(;9qM)~@Euy$31qR?K%gMFRqv`4slJghU!NfM`?=Ys`0zC_K zXRT3u%D(M16fD}@K;X#42s+QTgaPyHPs;NJG?~bX^Rr`gLYCV=&RHKgoh^1J`FMM` z#Rv)6Z@r?rcJ(S~cyN7_!2000*T!?Ir`_c|UZDV{d#SLTlL)hl_6E<}aqz7NXqXO- z?d#OF;eCD1<>enGf<0#}ZwHm;w32=EN#0sySjTY+)*hr>uqdf4A*f~>InB@bJqYWN zd+&?8Ve0k|H7@&m8S-Hyb@w5PWYMtRfG#~`Jy0tk(jE3^6~Ua ze#`B-{QEImCCW~lD@Versg(4WZgmgdh^4uHS~6?Qb_SRfW>N?F+n#rc7U@U2jAr=Fmd!8a!ef`O%@;~CXc z85vs}o9O%IaUe=qS#heq)e0qGRNSyD11hmV`MIj<^4b~;{x#4m;5%1lWI_T0?!tNW zf^lTv4ays;}$m+jba0y&=7XxOyY2qV?9=3R0SJxIE$Gt4KD~@4nr)-Q z>>ow)S{uza1%X{@w!-;%%;nhR^d0r1PX}Q0flvQ(HnK604t^+jt>`&8x;Nf_VFmW! z(W5#z@b+L~g2ao?g`134Me0^sMTVV~Tvk>F?{keZQ93FmW-vj55Dg9O&^vNsVge>C zfYavT;URwc9q;;e_{}p}S=m{rgCpYP_M*}2@HG~!xw_54U{z9wzCo+n9e6+vvtb0D z>;&d@1isI0G@q@_O;EG0!WSDzNR+Nu90NV^@j4dP`6>xHhaMBYscprG0HbytvIPLKDO=glCnYP|^nHzY|%UBkfWiD|!#??;3d8~oTJD_J2` z5gaI|zT9JPEG>tN%)gtzg)0R!@`bPa+xq(pA@c6rxszG8)kLOL>ae?XM=sGIUnN~J zA~G^9W_^0PyVTZ9NvR(O3^-_L#N>s}Ql!Sh{Zyv93yGb+GE{l!GTi2QArC}Q#nBRD zHP4Y6jZ6!qELdDnaEg4L9Tgi}R+rH(D{*ukL2pcE(U{fV-|| zC*W>UNz+Drb-Rt`C>r|Sn;$8W93FCoT)lE7luhfQre>^%hlj4wWns$urPugsSKmHE z7Z>U36%?1J|X zYAIX}_QTi@<9u(jjD~XP&wdlBpPo_-D~LuQhK0Ypk9(p=D|u?y7aFRo(56v#@SU~d zet2M@&C0+rT+=Sg_Hx4kGP~uzc?FiB7}vF_+VO`EPkek_1I^OZk z{~9F~)x)l?g2V)|h}}^)Z%{tfzjqQz!;n5}5)ccdL-C`4PRyAGo z+Ss^7Df;+v2!+f2e1lH(3m3?)p|^{RF*meI30W^yH3hMvp*m<3Tcj(belsiQGFTMnhBxOuz;NBNas4^Mi6tbMt7ZQ@O zpL?CDD;;&z4_|ac#L}Co!A&V@XV(#WcYBs+zFos`uuJMnXHJ5uZ0GDz;Y}DvEv=~$ zW$e4OTOJl0nU`RW8{`_9W*3^>$VsBCSCeqV4 z)bFfcMhMRm$)M{g6*7*sSVgG?r3T$@T#{M-68~(PH%+7Rcx6!IrK_{#^R93|GO(cS zalB-pqD|_XzIH>jv$lBZ60A9)3Fg<{@e#B^)oY#H>Em<&15>U zl`qbAP_S%^i>)5eD}2CC#mxwO&j5Pm@!=r>KEAvXOx#F7i-mFR+7f8y-HB4n%G}*c z1KH{MsctC>yvx6~$9sJIpNk!2ed?q`+ih%W+S=ZRFKtNsCi#E#_SR8Rw(S?_AS#Fw ziiq?(f~X)N-6)DUsDRR40@B^-ONxXJ0wSqMw{)j;cXxMpoNIi)@BQNUJF(6>>#XG; zq7O6AJafms_OK2666wFw;=xwyEr z=$t!uj{e0LqMJA8Cnhw*lqy}i+i@yrNUSF?cb~dwW<#<#HE%b^SPRtvn_=($%Q6>t z4k)?X##(#YXIvp|@UCX@ZDr9N{>q@s!QUDxb!%TCF>ia|$l;GQ<3MiFp* zcidYIre92LZL^km*^;gqiG@X6WD_|)!|8BxIAX1E5W&j`x&~^}{A3erY(zgDnzB3j zb$QX#*dz{3`v(u24D2{r)=Fc=#*U_grbE>n_DuRB{%bi?8A-uKWHPDT!I44I>kg2r zQ!e#1aWgUTv8-)>V2NL};={qwANV=%=0+gsC^3>}eA*J8&u8Pb8htiQ=W$a=pmO9y z2wCHKS>EMjM?JW4NnZKYEA!drw$@gOP1oD^wr4`coKAL7X?azdPA;c~q-qKlOLB@J z)=X2S07b>ukX_fu-a-r>K0dgp=R;pZZW#O9UU=9eV>LzB7cxMNrj+u`(yk6d*8)=? zlan+5o>&@tnV`a2_ktX#Y$3Zkoi~0tw z(VKkRJK{4l){S?Jm2=FLv$Wz}9wU*e1eEnDGA@%fpZE98u`iyFPcUt25xh!G4GA%0 zWxInnkq`;6$C#0tS*v_5hF;Fq#?rX^p!4coG893gNGS6P6(!}gd*AV~<+pEGB0ivL zy(x5@N~E2}u$6*zwR;&SERRjYdY=ohI_oP}zgW!e$y$Z{EC)+qAb&INDvg zJ&JO7r3vKlzMZLJ|u--Mo2LpPP_3d!k#yIdjZY?x%ia4- zC$uoK*C)Ple&!gSvp)OQ&+F!2XdY{Iyg%LF|0&wFY#ko0(B5!(Z~9du8We%lyNmdg zK|yy6IYc2=C+?AZG7W!9(MP&IIaTY6269SDyK~V>FuF%hPH#VthZfc!9t93NU`UKZ}y8a$?9oLH2k_NKb>F&wI-s^npyUiWaXz3S+Tie0N! zfjsA60@|5(@Y&r8?!ofSFBh8=yL5d3hr@l%( zx^;ZeL@3a%f(sE}Y7ot3J_RtH_`-#`p&b2=!mY=bNlkp;la=W-J+iNKTs6NYlB&2h zFw-PI!(012lF$0l@!A$0pVixJlabY*KR3;%PN^yQEspogZ?fntfuT)U9yc1Z_Ye$JyI42sOZJ5z zY`3$bp;xQZn;{3WTI{szSgc4iCBpEv(cK3ntPvqXUTF`1laF;zxCnlN4Cz7C@HnT4DEg5wmfWzU2$mK)#H zGK0p`V2R{L%>W^ihwm2cxA17mDwW`>LYad*?^to}s*Lcxmo=X{XWm8sX&RB8h)lma z%P^7V_PyKq__<~G`9yp)rG~>{7YAo3vJ%l)i45^ou%N6HryyzM-=}N9iq|WN-XcAR zco8rv^$3j}bDmBWVXYA?#2cr1{TlQw=;g?iUu9wQp`j8-bo*wfxD6=IOb+wE?(QiT zPsEm@xw4LBh-|~TGK(BaGZj%sq5 z0a_bM@JTT@V@bwZTolQcokeWiXcWL^sADbeqI_SG`*t~@u9o*pIhGVBg^;ng#(DMD z7H)iB7OvvPD>1pAAu1T)0UF%hy*lDsWY|VGkYmDFs2w3|G16SKNT`|pqB?D~dC|;A zhY;Yv^%}Z6EUDM*4axL^Rk+!+Ca?P3rbw^AW)h{+s&p#A*ySFk%?Dv?oI8AwGdVSt zrc8{;WCWz7!}0YNP4wN9BuW&XpuI&J06$Jr>C3X}y|L3gDbqz^!6RHMx^pf0#IOt+ zslXQ8+#Jbm@>!|)mRxMdb=2#`Q}rh`h9W-7`Hg(m?-av3f>O|;EvZHYAL7V#S-r zWjx$e?h4i6ywBB3uBS;<0tI`pz8#$&iI9dm640D(EM-WFqi^zL>VXUehaG>uy=cdI zvz*KL>t!UhqmdU8h*iQ4-$NiDyhu@sVL_l|_vwk42N5stL0OqL1vf#UkKM{_i?WE1 zX7wx{(qQ17e75juL1d(}*~(x6gerdFG=y-|*BUYT=6OOc#|cm)7Zl<jyiaz-5L=*Ra>ev!o_7W z%*w&>2M5PP+^g58iOI>wXJ?a5V^!L+w9j1DO5N3*9$43ZVI}OSd~&g@xM&vc+IjmfK~$fvxS}U{2_CKLwy6?Ef=Ck`|9ej>9<(UymHh z*3{h*Kh1YJp+?rc!+!2bWN*K=aOcK-g@lglRJ>?jJF~U=db+ect;OzNl7b{9rekR0 zJEw)3)znCMP-vpb_HQ~n8Err_#dj?n9}CQ!5l|2F-?~-9qbwO!Qs0!alCFBspL~Qi zH1(xma6?DO#DE^nSk@G%{DdQgUG1mqUA!{L^HYkZ@TSn4UbA zrFhj8YI?Gyendwn5dCQ*D>3K4i?2Fn5Xp*^}?LH6Hg+kAuE8cl;Y^)og2n?Wn zqk;Rh+DBhGEd!|dgTe~CGKD3xs-t^rsZ3AzVF3Ea*EbpF9FI*-GQzehyssevgS)EA znm$7k&^Zb%qhR;Vu+Q6*Xst9qyF|tDcwbUqzr)2PicpYav4vZ>#4d{tpcMaFGYHnD z=UNGw)Jg_6cR12M(N>Q-@6pgWWu{2d2ona%d9je?8^U+CHJcV3$T;eHMtp)9RQ6iG ze8VIp81`eiU^B(TYptt|U%K-|wk5-4w-o?glYqQwBN^|yQZh2Wobs~f*yd`W)MP)b{6%@th0zzmTK=&0D4ODzN z10}qVR);7!68jc=qP;x!U60acE8dkPMB#C1YbR^Pe^`fdAm|P`m(^-dQ(u8wLV}~B z;z6xH#2QOpzaC-Q6));dVBDKF1eRVQli@LKZLt8FGt$E8UmtF-uI}|^3*l4n32(g^ z!(a_k?jjh*0QE-gXd8Kyj}GkPe;7sZZVfOg6O0-Qk|gR$M#I+(es|#z=18@L+fz2V z#zWs5F+(OnLX}7)@mB36ciLOkk7F0fWveG;nRR-|<@vJwkZEJJX z@puCkgV9mK#iIzTB5cfG=4xsK?z4;;@`$O|j+Hx`m>i}lN7D$G@!d}iKkhtQYwKMg z;4pPrJ3esjS|G5q&rPv7xnY#9F91@iW7FyS8ivbsxBX4K>jwb&s0=fxs}qWedCxRy z=a~vq-ncQfHs&&2&x(9fY{N8^@2asquQWn!F1E11&TnIMlwThA51MA(leza2Zj(j6s_29*Me2Gx3`=_LxC#P8^DAW zxt#2MBsv?{D5u~SpTRs5xwr3=deUBT9mtKFgpF77JZxFqJ-!_zkc;A79HWE^f}bbi zAGLxUOJPw-%CXZAhCTzfcvVk+ga6k*wE%V&v+)2ks^TJHb(Xp-k#>$X`d_tL z(w$XRFz3s~{6-3No?Hgo)3JdPXA23@DfcnZV4~`!^{BWYsk{)p1tRk+Y{kNdce8-F zfz@A5alA3u5S%O4YOepdWM&fi%&p}zKK@PE3VlhkZB`Cv1BFd8RF4$v0@`@__e@6| z-db#4MAl?#T6z*8kzIc3oeNe#W~iuEkBmgO*6%Cen(r+7e|xNMYmL9R@qV!U%x7Y< z*XRc?2ClgU3vNppMoNHrsfUW+;(M%jBu^GSm(BEWVTvcwzyM0b=dpJPS^4%>+ij@Q zVc5#2`L;ChB0Ur`H6ou%TTMTk9j`2m-AW*fwDF?kd?7Y}FpZ9InzDlfk!v3S{VZzO(I%XRFow(L56K5p+W$s-LG(i?_Ld1)k3cc5JyGR9G4TkN6Upe) z*6*S$9$ptyRSKQwT2-90K9D~5Q!g@)-3HdU*p_AR=gjz@7nZcN#T*=pLW6@%hk3Yx z2z^v}z-6oit(8Y=!y_jyDV&`VfH94H(X<(uF=^BJhF?EE3}(C)ET+K5Vu(wRFC6;o zKv~i_B<^Ht23|t8MjRBX7Kf}139~wEJ57ZS=G&SM7idbgUx)Rere@BQ2!)!e3s40< z3exg#02>LG=j{(`BgOqiOq|3p9z>_YRNc}xcU{`;p3FdX%%r%vR!lr?M(@F6sq22) zBhCT_hv&e!qg^&&$66cM5`Ld%KIL`z%lH~pyfo7O+Nfve5V=#flt4xnXlNrJziauF z@}t66_WLpUAOpYC+)8GH91?}@p)sLHeYTm zdzhWW?;y81S$c$uz5774Z#_my%z z5jx%qwfa&M&b`u4VF(4cYpZsdCKq1#(Bbr7p7MEWf(^IE$BPgb8$R#0*bbBQVX4A~ z7c#)|AsPaA%Tg^SLYY*TJ^A2kH_+OAe0G@8&Zc@!r4t7s3pc}{z2jJRrQ+4-!h8GF!4HNgKUpG2G1$Pkldk!DBjhrl` zrj}Z(y-!NHIPMCA3;vwHU_VxNlJGb1J6m>(ecEC;>hw%^RsvZA(cNAI&LcgX!*zEI z&Q59N(z(0cgm@p0FiJ+3KZ^6H{{Bv8#&GdKbw+Qw?krR z2X52R!rva>QNc!-Q?ZDa zi)Y$vO=LJ^E$L7{4}HFR(cOcvgGW($6?PU}yJ=E9{7(5x!*2{%zT6naYI$@CmA-2l zIb-WPX!MDA9jbMMUjra%)SzNm5tq4sIE_g3DveymcH{ppNq~q^LDMx5&$e!CdM%nK zsU*H!j;;@O!)V8`7udI8C*Wd5DBcG42cwgrnhBIlr zYkd8hp*$9sL_(J$eVL^PB6umA>*|%&6A5a9&mj}TEE#tI0`tpe~}m% zPGV%RO=#H%$TOELJDU-u0azeeK$KMI0D_gZ(EjqXgbmdkF#uZZaCdYNX(VYA7e5 zR4CG{j!I0t@avayL?neDO>~Ll9th-eRmucrA@?K6AgNHq=i^6xM!9C_bZDxLF6^$1 zgfd5VK(DQEJtZTc$ZjM5d8i9CONGn!CqWlu>3!vWvDpMI)SE3G%rFYsC=an8Mp~~N znn``!^SES6N}?L@zf3|*9^hYF^WRIzO_t!u$muC!>-qMd;H`47&xJgmnU~?a{x?6Z zH?f@EA;p~dA=4dn`*xq0-?L*_1~B#F=}B?;z1mCg*36i+Z}G|0;a$B z0$i-9v6t<>6I(bMWx<`~Sy$BdYqJ zYsF-D(&g{TV*Dy43X=f27dkY zuF%{Rba2ttT~3lFJ%@b<1HratJ%IOS40bv_c=zj9IS2^f0yi^jcn;@0kYQA|gZ&-J z{+wc$?4Gy0AB8~9*(U?;AuCKT3Ky?F-|6MCRM>`gzn_-o&U?IF{TvrHS&7zaAu#0JnP zfOk|Pf=5$euHjC!fWvkokrr6v*mawEoDa9%u&_l$M4%{#($*aL#Kq-QJ5cb(_3IpO z>OUIxrHej!f{SU!e+&)nOOT8n2VL98k0m7n_PJ=fU$wP)`dzwU;sxPFc_F0a$b5zD zHqHeEd&*E>YUgVZcK8Ma5D*fo=bNZ1Du!)DfJO;ql41#6z;R2%FJ8WUncrgCMAgbhsUsVJJ5+F6Sa z14Vwm?fBjUG7QLI2foMn0IAclBs?5sy#9X@3$ zW-@GnH=HkW6^1-qDS}Hw6Ae9n=wm5?dp)E?H&SA5X~}VY{1LJSbTOb^NgYe1oN5IZ zE-5L1VDM;lLii8E9O5DVv~X<=&zJX$gjO+5@#JI$@CEYoAC>NnLc+0mD6^^`9J(?9 zG-$6Y8N&lmQ0ONVK4t~~JkldPd^Zsj`tqp4)~i;>l!&k0UPT6mDa#BE57=0{>v*1r z(bg(jJVtas9}@)NF_PT#ZUnQPEPqBFw)vQ3!eWXbnF>ri7p9$~9F*EcHx?o;Xy(>~ zDT(OgE1rL*yVw0zLX~eC*UUik`27$ArdpGY#gQnzZpWip@9`*z4(H)Wfh^H%wfyBq zF|<3Abaz)$nlhR4i920+iU)7^Q!TCU0#|8h)MsqHsCg1$UE`9Npjr;mB<%aZdLxBR zpF>ng=+!2;KlYf^jv*aPm_6;skLg2TTgr|%aZjEw^<_v}JGsU@6-c}6#+10tL5D(L z;eQl&#u}mBaX63T2$@kBNh@_y$F|3C}AkXRDxclJh)?O6T9C90HqY!tDNijW&UYl?pB$XPN`DvzYPAo(4i;4&`K2X; z5P7m0|MKXm-?ROh&@-@VZ%9jC5I(UQd!RjQ+MlV{`6D4&r6GWdx1*!_;uYM%ycH=G zmD%Ni*q_-!PEOwmsFdiV{ROS&2~J!iK6xAe(NL)duoPw|xT?y^=BVf9qL$8jm4iTu znW}vIFXo@9s_sr>UENg8drlD_iOAaPH=>09Y6#@2gcxGAc<0DxD{;L%P3JOoG2NQo zThvQec+dq)DG--&aiTy{32nD0LeqME@)!EgKeuR{SnvE1^H_7Dh+OU(H8uF`q9lb+ zlyJ?DZ0xBRGK4D`>Mir?snZEVK22+|U>J6w2m!0YsV_sIz2P#jY6Ffb+a~UGsW~kA zxp?}LRZ+GcORq~({7cm+$JI+)J$SjAZTgZXgH(oUGn(%)sveJW*Q2=Skbp&f-vp5O1Lj@(>rt4~}ypa`H@znO#5^J6kMj_d6sDKxY$9kGmPs^5FRHjvk#6 zNnb-H^YKRA%JQtA}u`WbJ(R|2LHq|Mg~p2y;1)8U!>9iQ7o3Yz$h5f5}k?P~r{?%L3*P z_ew+wjKr33VsZ{M09v|me3qu>gVWO$v4ATnCY7k5i)~+XkHhq4fg-*IW%TSE@Q8V) zXBG1uTRX(IJ$PD_GQL8*{hlDGe3{U7aA4nf23?1IqVIZbNx{tRpVU`|@;|^s?(g4j zZU!G?acfw%#lX+d=}_2gx}kjwnmFxR8FObzYh_HE80HrAZkZX11nf6~%g7@m%N9DE zchd|5Aph7q0BVQrg{kOpM-g0zS>yAMpRfPa9L$(6_LQE4)HKON@l_A!V|x2B4b?bD>sBzln93Ln&7#A~M&7Th2f&TN5xc$dDxk}Kek^XPYD@WPGxd6t4UF8-a zhsK<3YX$ds$XHeHe>*Zz zDz0-w=%2OQ0Wm&n@Zhq0&)y0Zt6tCIpQQ)`nedU1Fivs#un|q{%MIFp5LPf0ND$;b z6f$_Qq{AmWDv0w?PJb;s`jH}53T?xqoo!czVhkjfVq zF7x26KpRc@B^&3QvjLSZNeWxe)mcPSrax5+mlBraQ?&cF2Jd>#v#Q?pWX&&A4pbE{ zjQ&7eozlRD7sw%BeU^5x*Qc@Eg-xFkyhxbcM0?UCC2kJ47(_W>Nj-2O z(|8Woe;=g0+h-<7srLfW{qSr_^X9^ShZ~gjFoQAAE_|Q3T0)cHce5R1iC2$?m=%r!_A7m?Ku@m|VG^SxfPt zDUF_3mnRz(s^Pe8%7!kUuO#SwjCS{z9^c^$5sSExEEB!zH079I>O`(~imW>1Tj!LbBN8S#e)5hS|Y7`l;PStWxh zixr6TE58c?a5rZtRl~`|hq%Ta_4lRzM=dCYVG{W0VGbF6Sbqe?`jvX}3cjXV1-=a- zb+>w1D~mOu|KpS7$HADFxy?rH)?(r~>(}#farc2o zg|DI(bxoap^n7BVMOjWr>U#?%RhqmGJfA`5&)W@^E~p!C-nA$mt(;Ert=bx^~(XtKYVO}8EHklKyWU1CA5Kc9prxtEM@!O;kCeywE0{@ zLIO@Lv@tgauYi7c;;T60g}Km>5H8bE6*Y16kDou;ATJ8}1Z;S6a=lPzK_18yctRNF z6h2u9@pnrb3yHUaMy6YDxj*X@SVt6yj~Zf6tK&LCvoWC_+S;PVRLhH zm>Y11s>L>G76cj(YHYqlY6TJ)r%E#aLr|P=psGRpIeV^GDrj?a6GFlEAckOL@|S9n z)q|+50X?vEaxG8(`T!wcn0gF?x8yL|Xz*x&q*wTOy?T40%ah2&!~}%FMx8(22L`r5 zjqP%@0<2~Hf)yVM9m1l{?9Eb26%c&+J8>(u&cb`DSXR6M1anSnD@Szw`{@x1hke>=z!a?*ycjFIO z*rJ?wH>mgtXoOF@4z@zY0+#huW+Ce3)hkI|y*rnXPzLF$Xn-tdeqQsAMCyBgDqh=F zgQ;^!#MrsG z-hA=o7GB{zej-SCi9~!rsbFCt;j~_v4#If>0VOh0g)A*y1%-TZc5A-KQ#`Nr^_a7x za+cWGF*r=Jer0H;))4p7!e)jf@?jvA?{ z68CGZz@;L6xX3z%>z9_6)~Zd*p{POOrHUfplZiyOmlr_0{{h34C%!sKxj#PL{ZQC> zL#qK08%A|>aR0VWtbpa=wyhWbWz~Z93oIEnvgt^kf-T!I5~m~MM`r8gIGpR-R)hq* zj0}S%_Lfy8_QXI-Ow7%Np?_lZMXllad7YECD#9^$XJ%$V90^@)@Qj5ptAgdl^5k#_ zH2u`)fy98Ehok-d`urI1Dne}gQimAL3M?%K2Fa&k_>;ntZ!xa4vV@mWiK`C?H|did z`yb;|q&-%4JA{2+1T@2)^`B0FzQU+$uLD5>Xa5pHO!ioHtn8B~aMVf?Oo`*pA1HS? zM)tIs-7=iR{Hh43A}r%{{%`Ro@;}4_`WyV|G*SVnsrQA2%d4uY$Q%C(XKRiYK7+jZ zoq=ATtF%BnGyyMj8L0-|GDxomMbBo%J8XCcKzxyi-k^X5dpZnFr=P2Y@t~E1nqL(HAc1_N;4`#cH_f7YPnjQ=K~zzN<7d(07f*vI19EBj2mjP zoC(MKEJ_6|I747dS5Z-sh~y)=dGoID!XpAF%s2LtE(xs0iSuKn3!B>1@T7ncT?OA- zef_g)(P?i%5AQ4;5qAXQ`VG{RM>5{>4*k_s+;|FCFbV})h#Z7t{#GD-2%J0dKYrjM zOG-<@)Cq}M+dAN{8w8$feckM!JVh?;tB~^{g=q;=c=lW8EzHl*e|`-tAu22^EO+>> z2Fj|UYWez5Tibwa%*CBCOUAT`y)jqlCudD~{4&rowW~9_2pA17m_LR*W~gW=IWKPND&;fdiJ zhNz4J{+^M5m_?>*H@>~59i8M^W{!^&@kZSR3kWnWzp8I!A_HGwNlFgk*G~ zfvk~4>!p85>?|hW;5boHLH!ambRC3uWY)`^pG!pS&w1fH`jMXR4&bNO=(|~h&3Y+v z)lDLysfnNJI)~0;cloKF?n~{J*_lX}xkxfhr+N6`8yu2ALHdPF$A?G@_&3uLh9$5N zyl7r8D3Apo2J9ZSp7N<*7GQ0Kr=K(OALP~{2a(i$H05&WL|f@;9w)-uZ2V!AL;}CV zwqJ?zh-F~VKdW}7kz1gCyLXR5uhq$>DJ0nx&FYk1?N1R0ZI|Iv#||jtN$)F=Qt-cy za>X=-CZ@Y%8dq%Q+jU2ZMs##$rfN?FL5Q-y;o8A653JS+IsV;qdXU<(v$H?d0W8nf zoNIxRqE%PC|KGZD23xf3y?1^|e;_lpJ=pvL z=b?Ce{@Opvr{QHwExLfUgSP9WY<`KBS?A1-6XJbZ{U@qBb50!nhP_Sf6YBL_5s^Oy zjIkX*GPqd3q5$a0Ta1URNnAdHK=9XKI;y`roc~=)VA`8*#kGlpZX%f4!?tDjDKg83 z#EtJi_0#{S)x}NE^9ZxPn|at$xG>ujUl?Bkii=iPQo;ZP`+F!fbSi;5 z_9TKg1_TatE5QFiY;5e52x)inT@@7CXpOHB4wA6v1Itdb^#d#yYvZR-6F^73LgoL@ z4o)T()C5#w-kFwgs{;cAYtXs@xL<6wpd~N|bOUUq{l7ktg6kcQCLe-56e_(QgP@?G z>Z&T2{RwYK9Jq`G>j)HZ{XbB|&`E+?1Fi(B>m?`;(R57eWhw-Eo2wz8KAEqNSHkss zi#^ZLbV!el2v7}3l9+P&<@5^goaJ0d2RX5|dSYK@HY@=ql zvM;`0ne>z3Fbv?vYEKHN!%vmAei}G!XG2safVUWP|Xx068rqHmk0qAdv z(KzP-;<9K9bNO)i9?XwPwh(scu)PoP=SpZ=Vj^(oe%aaCL|SlSNT9%GeQco6;@aBT zVoCgX^@0TioSo0|ZMVRmE6(`qK)GXYO6@cr>cGDF%c0MWzHH&E2~|J3;a8?(Rv$!xeO zTo1T)X4$YLIWsepRxAMezmN+HaoCIMgHT5ZY=He7Y}mhG3Cr}7JHyprz&r>y&w-9M z!bDMP$0gO3U*i2UElS>y?whWdJqwr^$~j7Ili`?0?$a%h^~=M@2i{H;TKxHQ3QmLV zqqXvUXbeMlw~@hkq-X~oh5=X}f`X(0{ja)8CN~m8TR4Q2RD_3@7eZ;3U6N8$n+FCI z(R45&AW1=gMtmPv;~Lxtg+e@-XFO9TOuf>3`+yo}EM=BObPZH)3lO1Wrp?NCP5kJy(?nShlW zX2AGZ@(W?{4{s3|)(~+fd#^d^@F`Bwj0VR0<97Cq@L^-K-e3P2JknDdEl%KM)6bM3 zInkSrd*9>LGepIWFH+WxuUnXp<3-~}t5&RgN6s(L2FMm};w z#^{5A+}v)jwdL7MQ=5JqP`e-y4AS>X$;y88vyZT1l$i)|`J@E@f{Q?FA}W@D3lI1A zA@HDv?M3G(Io~Kk5TpB5{>vcrAF^M=yT_xB{I~B}ZBC1BxUT(T5#k?}00k2+zy$(lYFG8BLbW4iw@u zJF+Pxn*2*B>_y!8s_6z?TLiX@?d>pdC<5am|3Nmx^_o|38|;OeZ(g9TtF=qvCL~a}pKx%vGWz#i;G0?7oXZy9lBkKB#Of8dFwk3dwVJ+_bOAQ1SxTLGw3Ekbm`|8=t)$&RKnOJ%H2uv z+o6`X{LNbl+M*yzl=!j^u6W_~7H&q6T3Xfkc!1jy580bBHUf4>NudF4a5V2M?cpM! zap^(iO~BM8x!nJWm`ZRN8orPMFxRTegc<9YPBXN_;YeXulm1Mx@-!&M(i9%B^B;{L zY{{qg+(LRd8RS0pe#NdUFg4j}2D%~Oc0SRDx#R2w_`T>fU`$Ra&-x3TL;BHwsiaxe z=BQF$)=@NjfMB*FTh0pH@wf?D4{6Fea{8ZC#(^n=vL*H&xsbpq zIE0=4HlDiVVmfp?+^w>pL9?6|DS3VUN`Ps&9L zs~dco&>!p_h3BWHAolEgkKFO$Qn`TOh`alVuP+d>5v-=89#AIV>^lPqmj{tyuTu#6 zXNTDb2?=};BCzPG728*Qcl&x!~xI*<35Q1W)a@K_j0#=&RGD zc1`4sZfz8wHImhA6UaF(Q;E0j6&o;~Oo4_`=%)pF>furTT2GTLBnNf+;ywE$|AnofDs(A+y>Lj zGn{)t(B7<6m-NQcU5%R~QY$3V=Y-Kms&g`%7(%8pptxd+&dn&g(w!# zBNj3ILf%uf2n5Ysz$x#~o`Y_B8!mDaVoVm;nIMz~w31-@pV9Is7raLrCCFb5V(P=~ zI1xBoq3?GOBTH=uYxu%bBo>0=I^}Y{Mr92gt@%a3UK30_HSh01R~Cn0xe&(g57g!I z16@zeiL12Kf&Nqj$vy41W6po)v0OIn8UEN3cn7B&4D1LasdASQ4zAejkZ4;=a_f&Sa*o; zNTzdIt{}c#fp4Rtm&0VZ9qK?0BtXqfPHxlHpHLkt;KHW^tt|#%$4Fh7!x{!v5+de_ z7wtDXdU~ZSdN=g%&9UA60)}u#^yB^#y86!N(icL^WmeF4gnm_w6o&KpcdPLKsB%l; zVbI=O#{Lb)GcO2S#li&tfGy^qEjW}H)utQoGw62#Kc{4T>eh)d?EIVi_`hHB|I;2E zeyS=~{{<8}6?02QAAfW$q^e>x$=w(J_ksNX{ASJw>pT}(&*4(F!^y-ci literal 37738 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 literal 32728 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 diff --git a/doc/devel/uml/fig131205.png b/doc/devel/uml/fig131205.png index 17826d19baf8709bee8784508a6c4800dd7de253..5d46c324929c09b91f96a5ad2619e0b7ccc42a69 100644 GIT binary patch literal 13639 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 IY5e?u0gRfoPyhe` diff --git a/src/proc/asset/viewer.cpp b/src/proc/asset/viewer.cpp index b5c74ad30..bd1db8cbf 100644 --- a/src/proc/asset/viewer.cpp +++ b/src/proc/asset/viewer.cpp @@ -36,33 +36,31 @@ namespace asset { /** @todo anything significant to do here??? */ - Timeline::Timeline (const Asset::Ident& idi, RBinding const& sequenceBinding) + Viewer::Viewer (const Asset::Ident& idi) : Struct (idi) - , boundSequence_(sequenceBinding) { - REQUIRE (boundSequence_); + UNIMPLEMENTED ("anything regarding Viewer Assets"); } - PTimeline - Timeline::create (Asset::Ident const& idi, RBinding const& sequenceBinding) - { - REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation"); - - PTimeline newElement (AssetManager::instance().wrap (*new Timeline(idi, sequenceBinding))); - getRegistry().append (newElement); - - ENSURE (newElement); - ENSURE (getRegistry().isRegistered (*newElement)); - return newElement; - } +//PViewer +//Viewer::create (Asset::Ident const& idi) +//{ +// REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation"); +// +// PTimeline newElement (AssetManager::instance().wrap (*new Viewer(idi))); +// getRegistry().append (newElement); +// +// ENSURE (newElement); +// ENSURE (getRegistry().isRegistered (*newElement)); +// return newElement; +//} void - Timeline::unlink () + Viewer::unlink () { - AutoRegistered::detach(); - boundSequence_.purge(); +// AutoRegistered::detach(); Struct::unlink(); } diff --git a/src/proc/asset/viewer.hpp b/src/proc/asset/viewer.hpp index 5e5890552..a70c322f2 100644 --- a/src/proc/asset/viewer.hpp +++ b/src/proc/asset/viewer.hpp @@ -22,11 +22,23 @@ /** @file viewer.hpp - ** structural element within the session. - ** \c "timeline(theTimelineName),bindSequence(theTimelineName,sequenceID)." + ** structural element corresponding to a viewer in the GUI. + ** This Asset marks an attachment point, allowing other (output producing) + ** elements to be connected to ("viewer attachment"). The typical standard case + ** is a Timeline: It features a set of global pipes (busses), which collect all + ** produced data. Yet without an explicit output connection, this data can't be + ** generated, because in Lumiera, actual output is always retrieved ("pulled") + ** starting from an output sink. Thus, in order to \em perform (play, render) + ** the timeline, an "view connection" needs to be established: this connection + ** is represented by an session::BindingMO, linking the Timeline to an + ** asset::Viewer. Consequently, the same output mapping and translation + ** mechanism used for Sequence-Timeline- (and VirtualClip-)Bindings + ** is employed in this situation to figure out the real output port. + ** + ** @todo WIP-WIP-WIP as of 5/11 ** ** @see Session - ** @see Sequence + ** @see Timeline ** @see StructFactory ** */ @@ -61,27 +73,24 @@ namespace session { namespace asset { -// using lumiera::P; - class Timeline; - typedef lumiera::P PTimeline; + using lumiera::P; + class Viewer; + typedef lumiera::P PViewer; /** * TODO type comment */ - class Timeline + class Viewer : public Struct - , public lib::AutoRegistered +// , public lib::AutoRegistered { - typedef mobject::session::RBinding RBinding; - RBinding boundSequence_; - - Timeline (Ident const&, RBinding const&); + Viewer (Ident const&); /////TODO ctor params???? public: /** create and register a new Timeline instance */ - static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding); +// static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding); protected: virtual void unlink (); diff --git a/uml/lumiera/128133 b/uml/lumiera/128133 index b913b7406..377bc708e 100644 --- a/uml/lumiera/128133 +++ b/uml/lumiera/128133 @@ -1,6 +1,6 @@ format 58 "Asset" // ProcessingLayer::Asset - revision 22 + revision 23 modified_by 5 "hiv" // class settings //class diagram settings @@ -773,6 +773,28 @@ ${inlines} end end + class 174981 "Viewer" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 214277 // + relation 203141 ---|> + a public + cpp default "${type}" + classrelation_ref 214277 // + b parent class_ref 136965 // Struct + end + end + class 138117 "Pipe" visibility package cpp_decl "${comment}${template}class ${name}${inherit} diff --git a/uml/lumiera/130309.diagram b/uml/lumiera/130309.diagram index 3f9868b95..e0c065816 100644 --- a/uml/lumiera/130309.diagram +++ b/uml/lumiera/130309.diagram @@ -42,11 +42,11 @@ classcanvas 131461 class_ref 137605 // Preview end classcanvas 131973 class_ref 137733 // Effect draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default - xyz 412 416 2000 + xyz 412 419 2000 end classcanvas 132101 class_ref 137861 // Codec draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default - xyz 464 417 2000 + xyz 464 419 2000 end classcanvas 132613 class_ref 138117 // Pipe draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default @@ -96,6 +96,10 @@ classcanvas 140805 class_ref 160901 // Timeline draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default xyz 622 419 2000 end +classcanvas 141445 class_ref 174981 // Viewer + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 545 419 2000 +end relationcanvas 129157 relation_ref 138117 // geometry VHV from ref 128645 z 1999 to point 289 293 @@ -135,16 +139,16 @@ relationcanvas 130949 relation_ref 138629 // end relationcanvas 132229 relation_ref 139269 // geometry VHV - from ref 131973 z 1999 to point 432 390 - line 139269 z 1999 to point 448 390 + from ref 131973 z 1999 to point 432 391 + line 139269 z 1999 to point 448 391 line 139397 z 1999 to ref 128773 no_role_a no_role_b no_multiplicity_a no_multiplicity_b end relationcanvas 132357 relation_ref 139397 // geometry VHV - from ref 132101 z 1999 to point 484 390 - line 139525 z 1999 to point 448 390 + from ref 132101 z 1999 to point 484 391 + line 139525 z 1999 to point 448 391 line 139653 z 1999 to ref 128773 no_role_a no_role_b no_multiplicity_a no_multiplicity_b @@ -270,6 +274,13 @@ relationcanvas 141189 relation_ref 185349 // no_role_a no_role_b no_multiplicity_a no_multiplicity_b end +relationcanvas 141573 relation_ref 203141 // + geometry HV + from ref 141445 z 1999 to point 601 436 + line 141701 z 1999 to ref 128901 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end line 128261 -_-_ geometry HV from ref 128005 z 1999 to point 328 149 line 128389 z 1999 to ref 128133 diff --git a/uml/lumiera/131205.diagram b/uml/lumiera/131205.diagram index 51140cdfa..dc3096de2 100644 --- a/uml/lumiera/131205.diagram +++ b/uml/lumiera/131205.diagram @@ -24,10 +24,6 @@ classcanvas 128645 class_ref 139141 // DoAttach draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default xyz 337 431 2000 end -classcanvas 128773 class_ref 137989 // Track - draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default - xyz 328 168 2000 -end classcanvas 128901 class_ref 139269 // DoRecurse draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default xyz 409 431 2000 @@ -36,6 +32,18 @@ classcanvas 129029 class_ref 136965 // Struct draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default xyz 425 73 2005 end +classcanvas 132101 class_ref 152197 // Sequence + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 121 217 2000 +end +classcanvas 132229 class_ref 160901 // Timeline + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 87 168 2000 +end +classcanvas 132357 class_ref 174981 // Viewer + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 162 168 2000 +end relationcanvas 129157 relation_ref 139653 // geometry VHV from ref 128517 z 1999 to point 260 139 @@ -50,14 +58,6 @@ relationcanvas 129285 relation_ref 141189 // no_role_a no_role_b no_multiplicity_a no_multiplicity_b end -relationcanvas 129413 relation_ref 139525 // - geometry VHV - from ref 128773 z 1999 to point 348 139 - line 130181 z 1999 to point 445 139 - line 130309 z 1999 to ref 129029 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b -end relationcanvas 129541 relation_ref 141701 // from ref 128389 z 1999 stereotype "<>" xyz 367 290 3000 to ref 128133 role_a_pos 364 301 3000 no_role_b @@ -91,5 +91,29 @@ relationcanvas 131589 relation_ref 146053 // role_a_pos 328 250 3000 no_role_b multiplicity_a_pos 379 273 3000 no_multiplicity_b end +relationcanvas 132485 relation_ref 185349 // + geometry VHV unfixed + from ref 132229 z 1999 to point 112 139 + line 133125 z 1999 to point 445 139 + line 133253 z 1999 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 132613 relation_ref 203141 // + geometry VHV unfixed + from ref 132357 z 1999 to point 183 139 + line 133381 z 1999 to point 445 139 + line 133509 z 1999 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 132741 relation_ref 185221 // + geometry VHV unfixed + from ref 132101 z 1999 to point 149 139 + line 132869 z 1999 to point 445 139 + line 132997 z 1999 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end preferred_whz 530 608 1 end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 09a1aa0c4..b61f08080 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -4,8 +4,12 @@ diagrams 631 352 100 4 0 0 objectdiagram_ref 138885 // ModelAssetRelations 730 488 100 4 0 0 - active classdiagram_ref 142725 // Time flavours + classdiagram_ref 142725 // Time flavours 595 646 100 4 0 0 + classdiagram_ref 131205 // Struct-Asset Relations + 530 608 100 4 0 0 + active classdiagram_ref 130309 // Asset Kinds + 855 805 100 4 0 0 end show_stereotypes selected @@ -15,7 +19,9 @@ open package_ref 128005 // design class_ref 160389 // VirtualMedia class_ref 136837 // Proc + class_ref 152197 // Sequence class_ref 160901 // Timeline + class_ref 174981 // Viewer class_ref 139269 // DoRecurse class_ref 162821 // TypedID::Link classview_ref 128389 // Controller Workings @@ -31,9 +37,9 @@ open class_ref 152453 // PlacementRef classrelation_ref 178437 // class_ref 153733 // QueryFocusStack - expansionregion_ref 128133 // establish partitioning usecaseview_ref 128261 // config examples - classview_ref 128133 // Engine Workings + + package_ref 128389 // RenderEngine classdiagram_ref 142725 // Time flavours class_ref 134917 // Time class_ref 169221 // TimeGrid diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 83ca6e30c..2105f0406 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -5579,13 +5579,14 @@ Instead, we should try to just connect the various subsystems via Interfaces and * to shield the rendering code of all complexities of thread communication and synchronization, we use the StateProxy
-
+
Structural Assets are intended mainly for internal use, but the user should be able to see and query them. They are not "loaded" or "created" directly, rather they //leap into existence // by creating or extending some other structures in the session, hence the name. Some of the structural Asset parametrisation can be modified to exert control on some aspects of the Proc Layer's (default) behaviour.
 * [[Processing Patterns|ProcPatt]] encode information how to set up some parts of the render network to be created automatically: for example, when building a clip, we use the processing pattern how to decode and pre-process the actual media data.
 * [[Tracks|Track]] are one of the dimensions used for organizing the session data. They serve as an Anchor to attach parametrisation of output pipe, overlay mode etc. By [[placing|Placement]] to a track, a media object inherits placement properties from this track.
 * [[Pipes|Pipe]] form &mdash; at least as visible to the user &mdash; the basic building block of the render network, because the latter appears to be a collection of interconnected processing pipelines. (this is the //outward view; // in fact the render network consists of [[nodes|ProcNode]] and is [[built|Builder]] from the Pipes, clips, effects...)[>img[Asset Classess|uml/fig131205.png]]
 * [[Sequence]] assets act as a façade to the fundamental compound building blocks within the model, a sequence being a collection of clips placed onto a tree of tracks. Sequences, as well as the top-level tracks enclosed will be created automatically on demand. Of course you may create them deliberately. Without binding it to a timeline or meta-clip, a sequence remains invisible.
-* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID 
+* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID
+* [[Viewer|ViewerAsset]] assets correspond to the available viewer elements in the GUI. By [[connecting to a viewer|ViewConnection]], session elements derive a concrete (physical) output and gain the ability to be [[played|PlayService]].
 
 
 

From 27533c3bb9392d6d25b51fadecc5e50ebe1c777a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 28 May 2011 18:14:21 +0200
Subject: [PATCH 022/296] WIP darft usage for the new time::Control (life
 change) element

---
 src/lib/scoped-holder.hpp            |  9 +++
 src/lib/time/control.hpp             |  9 ++-
 tests/lib/time/time-control-test.cpp | 86 +++++++++++++++++++++-------
 3 files changed, 79 insertions(+), 25 deletions(-)

diff --git a/src/lib/scoped-holder.hpp b/src/lib/scoped-holder.hpp
index f8eaf74d2..67252b053 100644
--- a/src/lib/scoped-holder.hpp
+++ b/src/lib/scoped-holder.hpp
@@ -173,6 +173,15 @@ namespace lib {
           return *obj;
         }
       
+      TY& 
+      create (TY const& o)    ///< place new content object using copy ctor
+        {
+          ASSERT (!created_);
+          TY * obj = new(content_) TY(o);
+          ++created_;
+          return *obj;
+        }
+      
       void
       clear ()
         {
diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index e5f44629c..87bd271f2 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -91,15 +91,18 @@ namespace time {
   template
   class Control
     : public Mutation
-    , public function
     {
     public:
-      typedef function timeSignal;
+      typedef function TimeSignal;
+      
+      void operator() (TI const&);
+      void operator() (Offset const&);
+      void operator() (uint);
       
       /** install a callback functor to be invoked
        *  to notify for any changes to the observed
        *  time entity */
-      void connectChangeNotification (timeSignal);
+      void connectChangeNotification (TimeSignal const&);
       
       /** disconnect from observed entity and
        *  cease any change notification */
diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index a89b96e48..5b4784835 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -25,15 +25,17 @@
 #include "lib/test/test-helper.hpp"
 #include "lib/time/timevalue.hpp"
 #include "lib/time/timequant.hpp"
-#include "lib/time/mutation.hpp"
+#include "lib/time/control.hpp"
 #include "proc/asset/meta/time-grid.hpp"
 #include "lib/meta/generator-combinations.hpp"
+#include "lib/scoped-holder.hpp"
 #include "lib/util.hpp"
 
 #include 
 #include 
 #include 
 
+using lib::test::showType;
 using boost::lexical_cast;
 using util::isnil;
 using std::cout;
@@ -45,9 +47,13 @@ namespace lib {
 namespace time{
 namespace test{
   
+  namespace error = lumiera::error;
+  
+  using lib::ScopedHolder;
   using asset::meta::TimeGrid;
   using lumiera::typelist::Types;
   using lumiera::typelist::InstantiateChainedCombinations;
+  using error::LUMIERA_ERROR_UNCONNECTED;
   
   namespace {
     inline string
@@ -60,20 +66,52 @@ namespace test{
     }
     
     
+    /**
+     * Mock object to receive change notifications.
+     * A copy of the most recently received value
+     * is memorised within an embedded buffer,
+     * to be verified by the actual tests.
+     */
+    template
+    class TestListener
+      : boost::noncopyable
+      {
+        ScopedHolder received_;
+        
+      public:
+        void
+        operator() (TI const& changeValue)
+          {
+            received_.clear();
+            received_.create (changeValue);
+          }
+        
+        TI&
+        reveivedValue()
+          {
+            return *received_;
+          }
+      };
+    
+    
+    
     template
     struct TestCase
       : BASE
       {
         void
-        performTestCases()
+        performTestCases(TimeValue const& o, TimeValue const& c)
           {
-            
+            cout << "Test-Case. Target=" << showType() 
+                 <<" <--feed--- "        << showType() 
+                 < (arg);
         }
       
-      struct TestValues
-        {
-          TimeVar var;
-          Duration dur;
-          TimeSpan span;
-          QuTime quant;
-          
-          TestValues (TimeValue o)
-            : var(o)
-            , dur(o)
-            , span(o, Offset(o))
-            , quant(o, "test_grid")
-            { }
-        };
-      
       
       virtual void
       run (Arg arg) 
@@ -133,19 +156,38 @@ namespace test{
           FrameNr count(qChange);
           
           verifyBasics();
-          verifyMatrix_of_MutationCases();
+          verifyMatrix_of_MutationCases(o,c);
         } 
       
       
       void
       verifyBasics()
         {
+          TimeSpan target(Time(0,10), FSecs(5));
+          
+          Control
-
+
The term &raquo;Time&laquo; spans a variety of vastly different entities. Within a NLE we get to deal with various //flavours of time values.//
 ;continuous time
 :without any additional assumptions, ''points in time'' can be specified with arbitrary precision.
 :the time values are just numbers; the point of reference and the meaning is implicit.
 :within Lumiera, time is encoded as integral number of //micro ticks,// practically continuous
-;duration
+;time distance
 :a range of time, a ''distance'' on the time axis, measured with the same arbitrary precision as time points.
-:distances and durations can be determined by //subtracting// two time points, consequently they are //signed numbers.//
-:a duration always abstracts from the time //when// this duration or distance happens, the relation to any time scale remains implicit
+:distances can be determined by //subtracting// two time points, consequently they are //signed numbers.//
 ;offset
-:offsetting a time or a duration is an operation (not an entity): it means changing the denoted time point or duration.
-:the //target// of an offset operation is a time or duration, while it's //argument// is a distance (synonymous to duration).
-:Time values are //immutable,// like numbers. Only a ''time variable'' can be changed. Durations to the contrary can be mutable or  const.
+:a distance can be used to adjust (offset) a time or a duration: this means applying a relative change.
+:the //target// of an offset operation is a time or duration, while it's //argument// is a distance (synonymous to offset).
+;duration
+:the length of a time range yields a ''time metric''.
+:the duration can be defined as the //absolute value//&nbsp; of the offset between start and endpoint of the time range.
+:a duration always abstracts from the time //when// this duration or distance happens, the relation to any time scale remains implicit
 ;time span
 :contrary to a mere duration, a ''time interval'' or time span is actually //anchored// at a specific point in time.
 :it can be seen as a //special kind of duration,// which explicitly states the information //when// this time span takes place.
+;changing time
+:Time values are //immutable,// like numbers. Only a ''time variable'' can be changed.
+:yet some of the special time entities can recieve [[mutation messages|TimeMutation]], allowing e.g. for adjustments to a time interval selection from the GUI
 
 ;internal time
 :While the basic continuous time values don't imply any provision regarding the time scale and origin to be used, actually, within the implementation of the application, the meaning of time values is uniform and free of contradictions. Thus effectively there is an ''implementation time scale'' -- but its scope of validity is //strictly limited to the implementation level of a single application instance.// It is never exposed and never persisted. It might not be reproducible over multiple instantiations of the application. The implementation reserves the right to recalibrate this internal scale. Later, when Lumiera gains the capability to run within a network of render nodes, these instance connections will include a negotiation about the internal time scale, which remains completely opaque to the outer world. This explains, why {{{lumiera::Time}}} instances lack the ability to show their time value beyond debugging purposes. This is to avoid confusion and to stress their opaque nature.

From b54cd2c722042198b6cfa44da7f5dd5e97353b8a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 8 Jun 2011 03:35:48 +0200
Subject: [PATCH 030/296] cont. defining time::Control special cases

the refactoring seems to work out OK, was able
to cover all the cases defined thus far....
---
 src/lib/time/control.hpp             | 182 ++++++++++++++++++++-------
 tests/lib/time/time-control-test.cpp |  11 +-
 2 files changed, 147 insertions(+), 46 deletions(-)

diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index 69ae4ec02..01f5295cc 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -162,12 +162,53 @@ namespace time {
       };
     
     
+    
+    namespace { // metaprogramming helpers to pick the suitable Instantiation...
+      
+      template
+      struct isDurationZ
+        {
+          static const bool value = is_sameType::value;
+        };
+      template
+      struct isTimeSpanZ
+        {
+          static const bool value = is_sameType::value;
+        };
+      template
+      struct isQuTime
+        {
+          static const bool value = is_sameType::value;
+        };
+      
+      template
+      struct isSpecialCase
+        {
+          static const bool value = isDurationZ::value;
+        };
+      
+      template
+      inline bool
+      isDuration()
+        {
+          return is_sameType::value;
+        }
+      
+      template
+      inline bool
+      isTimeSpan()
+        {
+          return is_sameType::value;
+        }
+    }
+    
+    
     template
     struct Builder
       {
         
         static TI
-        buildChangedValue (TAR& target)
+        buildChangedValue (TAR const& target)
           {
             return TI(target);
           }
@@ -176,12 +217,21 @@ namespace time {
     struct Builder
       {
         static TimeSpan
-        buildChangedValue (TAR& target)
+        buildChangedValue (TAR const& target)
           {
             return TimeSpan (target, Duration::NIL);  /////////////TODO how to feed the "new value" duration????
           }
       };
     template<>
+    struct Builder
+      {
+        static TimeSpan
+        buildChangedValue (Duration const& targetDuration)
+          {
+            return TimeSpan (Time::ZERO, targetDuration);
+          }
+      };
+    template<>
     struct Builder
       {
         static TimeSpan
@@ -219,41 +269,112 @@ namespace time {
       , Builder
       {
         
+        template
         static TI
-        imposeValueChange (TAR& target, TI const& newVal)
+        processValueChange (TAR& target, SRC const& change)
           {
-            imposeChange (target,newVal);
+            imposeChange (target,change);
             return buildChangedValue(target);
           }
         
         static TI
-        imposeOffset (TAR& target, Offset const& off)
+        useLengthAsChange (TAR& target, TimeSpan const& change)
           {
-            imposeChange (target,off);
-            return buildChangedValue(target);
+            return processValueChange(target, change.duration());
           }
         
         static TI
-        imposeNudge (TAR& target, int off_by_steps)
+        mutateLength (TimeSpan& target, Duration const& change)
           {
-            imposeChange (target,off_by_steps);
+            Mutator::imposeChange (target.duration(), change);
+            return Builder::buildChangedValue(target);
+          }
+        
+        static TimeSpan
+        mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
+          {
+            Mutator::imposeChange (target.duration(), change.duration());
+            Mutator::imposeChange (target,change.start());
+            return Builder::buildChangedValue(target);
+          }
+        
+        static TI
+        dontChange (TAR& target)
+          {
+            // note: not touching the target
             return buildChangedValue(target);
           }
       };
     
+    
+    
     template
-    struct Policy;
-    
-    template
-    struct Policy
+    struct Policy
       {
-        static function
+        static function
         buildChangeHandler (TAR& target)
           {
-            return bind (Adap::imposeValueChange, ref(target), _1 );
+            return bind (Adap::template processValueChange, ref(target), _1 );
           }
       };
     
+    
+    // special treatment of Durations as target...
+    
+    template
+    struct Policy
+      {
+        static function
+        buildChangeHandler (Duration& target)
+          {
+            return bind (Adap::dontChange, ref(target) );
+          }
+      };
+    
+    template
+    struct Policy
+      {
+        static function
+        buildChangeHandler (Duration& target)
+          {
+            return bind (Adap::template processValueChange, ref(target), _1 );
+          }
+      };
+    
+    template
+    struct Policy
+      {
+        static function
+        buildChangeHandler (Duration& target)
+          {
+            return bind (Adap::useLengthAsChange, ref(target), _1 );
+          }
+      };
+    
+    // special treatment for TimeSpan values...
+    
+    template
+    struct Policy
+      {
+        static function
+        buildChangeHandler (TimeSpan& target)
+          {
+            return bind (Adap::mutateLength, ref(target), _1 );
+          }
+      };
+    
+    template<>
+    struct Policy
+      {
+        static function
+        buildChangeHandler (TimeSpan& target)
+          {
+            return bind (Adap::mutateTimeSpan, ref(target), _1 );
+          }
+      };
+    
+    
+/*    
     template
     struct Policy
       {
@@ -273,7 +394,7 @@ namespace time {
             return bind (Adap::imposeNudge, ref(target), _1 );
           }
       };
-    
+*/    
       
     template
     TI
@@ -371,33 +492,6 @@ namespace time {
     }
 #endif //(End)quantisation special case    
     
-    
-    namespace { // metaprogramming helpers to pick the suitable Instantiation...
-      
-      template
-      struct isDuration
-        {
-          static const bool value = is_sameType::value;
-        };
-      template
-      struct isTimeSpan
-        {
-          static const bool value = is_sameType::value;
-        };
-      template
-      struct isQuTime
-        {
-          static const bool value = is_sameType::value;
-        };
-      
-      template
-      struct isSpecialCase
-        {
-          static const bool value = isDuration::value;
-        };
-      
-    }
-    
     template
     template
     struct Mutator::MutationPolicy<                  typename disable_if< isSpecialCase, 
@@ -437,7 +531,7 @@ namespace time {
 
     template
     template
-    struct Mutator::MutationPolicy<                  typename enable_if< isDuration, 
+    struct Mutator::MutationPolicy<                  typename enable_if< isDurationZ, 
                                        TI>::type, TAR>
       {
         static function
diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index 1b5cb2dd7..e309fabaa 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -260,10 +260,17 @@ namespace test{
       CHECK (target == otherDuration);
     }
     void
-    verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span)
+    verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span_as_change)
     {
       CHECK (target != org);
-      CHECK (target == span.duration());
+      CHECK (target == span_as_change.duration());
+    }
+    void
+    verify_wasChanged (TimeSpan const& target, TimeValue const& org, Duration const& changedDur)
+    {
+      CHECK (target == org, "Logic error: Duration was used as start point of the target TimeSpan");
+      CHECK (target.duration() != Time(FSecs(3,2)), "length of the timespan should have been changed");
+      CHECK (target.duration() == changedDur);
     }
     
     

From 92dbedc289585ddbba8e519a8c8960d285a7ba46 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 10 Jun 2011 18:19:02 +0200
Subject: [PATCH 031/296] intensify the unit-test...

---
 tests/lib/time/time-control-test.cpp | 104 ++++++++++++++++++++-------
 1 file changed, 78 insertions(+), 26 deletions(-)

diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index e309fabaa..6787e995c 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -81,7 +81,15 @@ namespace test{
         ScopedHolder received_;
         
       public:
-        TestListener() { received_.create (Time::ZERO); }
+        TestListener()
+          { 
+            received_.create (Time::ZERO); 
+          }
+        
+        TestListener(TI const& initialValue)
+          { 
+            received_.create (initialValue);
+          }
         
         void
         operator() (TI const& changeValue)  const
@@ -90,8 +98,8 @@ namespace test{
             received_.create (changeValue);
           }
         
-        TI&
-        reveivedValue()
+        TI const&
+        receivedValue()  const
           {
             return *received_;
           }
@@ -160,13 +168,13 @@ namespace test{
           controller (Time(FSecs(21,2)));
           CHECK (Time(500,10) == target);
           
-          CHECK (follower.reveivedValue() == Time::ZERO);
+          CHECK (follower.receivedValue() == Time::ZERO);
           controller.connectChangeNotification (follower);
-          CHECK (follower.reveivedValue() == Time(500,10));
+          CHECK (follower.receivedValue() == Time(500,10));
           
           controller (Offset(-Time(500,1)));
           CHECK (Time(0,9) == target);
-          CHECK (Time(0,9) == follower.reveivedValue());
+          CHECK (Time(0,9) == follower.receivedValue());
         }
       
       
@@ -241,7 +249,7 @@ namespace test{
     
     template
     void
-    verify_wasChanged (TAR const& target, TimeValue const& org, SRC const& change)
+    ____verify_wasChanged (TAR const& target, TimeValue const& org, SRC const& change)
     {
       CHECK (target != org);
       CHECK (target == change);
@@ -249,24 +257,24 @@ namespace test{
     
     template
     void
-    verify_wasChanged (Duration const& target, TimeValue const& org, SRC const&)
+    ____verify_wasChanged (Duration const& target, TimeValue const& org, SRC const&)
     {
       CHECK (target == org, "Logic error: Duration was changed by time value");
     }
     void
-    verify_wasChanged (Duration const& target, TimeValue const& org, Duration const& otherDuration)
+    ____verify_wasChanged (Duration const& target, TimeValue const& org, Duration const& otherDuration)
     {
       CHECK (target != org);
       CHECK (target == otherDuration);
     }
     void
-    verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span_as_change)
+    ____verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span_as_change)
     {
       CHECK (target != org);
       CHECK (target == span_as_change.duration());
     }
     void
-    verify_wasChanged (TimeSpan const& target, TimeValue const& org, Duration const& changedDur)
+    ____verify_wasChanged (TimeSpan const& target, TimeValue const& org, Duration const& changedDur)
     {
       CHECK (target == org, "Logic error: Duration was used as start point of the target TimeSpan");
       CHECK (target.duration() != Time(FSecs(3,2)), "length of the timespan should have been changed");
@@ -277,23 +285,54 @@ namespace test{
     
     template
     void
-    verify_wasOffset (TAR const&, TimeValue const&, Offset const&)//(TAR const& target, TimeValue const& o, Offset const& offset)
+    ____verify_wasOffset (TAR const& target, TAR const& refState, Offset const& offset)
     {
-      
+      CHECK (target != refState);
+      CHECK (target == Time(refState)+offset);
     }
     
     template
     void
-    verify_zeroAlligned (TAR const&, TAR const&)//(TAR const& target, TAR const& oldState)
+    ____verify_wasOffsetBack (TAR const& target, TAR const& refState)
     {
-      
+      CHECK (target == refState);
     }
     
+    
     template
     void
-    verify_nudged (TAR const&, TAR const&, int64_t)//(TAR const& target, TAR const& oldState, int64_t offsetSteps)
+    ____verify_nudged (TAR const& target, TAR const& refState, int64_t offsetSteps)
     {
-      
+      CHECK (target != refState);
+      CHECK (target == Time(refState)+Time(FSecs(offsetSteps)));
+    }
+    template<>
+    void
+    ____verify_nudged (QuTime const& target, QuTime const& refState, int64_t offsetSteps)
+    {
+      CHECK (target != refState);
+      PQuant quantiser(target);
+      CHECK (target == Time(quantiser->materialise(refState))
+                     + offsetSteps * FrameRate::NTSC.duration());
+    }
+    
+    
+    template
+    void
+    ____verify_notification (TAR const& target, TestListener const& follower)
+    {
+      CHECK (target == follower.receivedValue());
+    }
+    void
+    ____verify_notification (TimeSpan const& targetTimeSpan, TestListener const& follower)
+    {
+      CHECK (follower.receivedValue() == targetTimeSpan.duration());
+    }
+    void
+    ____verify_notification (Duration const& targetDuration, TestListener const& follower)
+    {
+      CHECK (Time::ZERO     == follower.receivedValue());
+      CHECK (targetDuration == follower.receivedValue().duration());
     }
     
     
@@ -313,34 +352,47 @@ namespace test{
             Control controller;
             
             TAR target = TestTarget::build(org);
+            SRC change = TestChange::prepareChangeValue(c);
+            TestListener follower(change);
+            
+            controller.connectChangeNotification(follower);
             target.accept (controller);
             
-            SRC change = TestChange::prepareChangeValue(c);
             controller (change);
-            verify_wasChanged (target, org, change);
+            ____verify_wasChanged (target, org, change);
+            ____verify_notification(target,follower);
+            
+            TAR refState(target);
             
             Offset offset(c);
             controller (offset);
-            verify_wasOffset (target, org, offset);
+            ____verify_wasOffset (target, refState, offset);
+            controller (-offset);
+            ____verify_wasOffsetBack (target, refState);
+            ____verify_notification(target,follower);
             
-            TAR oldState(target);
             controller (0);
-            verify_zeroAlligned(target, oldState);
+            ____verify_nudged (target, refState, 0);
+            ____verify_notification(target,follower);
             
             controller (+1);
-            verify_nudged (target, oldState, +1);
+            ____verify_nudged (target, refState, +1);
+            ____verify_notification(target,follower);
             
             controller (-2);
-            verify_nudged (target, oldState, -1);
+            ____verify_nudged (target, refState, -1);
+            ____verify_notification(target,follower);
             
             int maxInt = std::numeric_limits::max();
             int minInt = std::numeric_limits::min();
             
             controller (maxInt);
-            verify_nudged (target, oldState, -1LL + maxInt);
+            ____verify_nudged (target, refState, -1LL + maxInt);
+            ____verify_notification(target,follower);
             
             controller (minInt);
-            verify_nudged (target, oldState, -1LL + maxInt+minInt);
+            ____verify_nudged (target, refState, -1LL + maxInt+minInt);
+            ____verify_notification(target,follower);
             
             
             // tail recursion: further test combinations....

From 15a3694cca9fe09b3a52cf487a9dc05d90c8801c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 11 Jun 2011 20:20:20 +0200
Subject: [PATCH 032/296] more complete unit test pass

---
 src/lib/time/control.hpp             | 35 ++++++++++++++++++----------
 src/lib/time/lumitime.cpp            | 13 ++++++++---
 src/lib/time/timevalue.hpp           | 13 +++++++----
 tests/lib/time/time-control-test.cpp | 13 ++++-------
 tests/lib/time/time-value-test.cpp   |  9 ++++++-
 5 files changed, 55 insertions(+), 28 deletions(-)

diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index 01f5295cc..48af1813c 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -321,8 +321,19 @@ namespace time {
     
     // special treatment of Durations as target...
     
+    namespace {
+      template
+      struct canMutateDuration
+        {
+          static const bool value = is_sameType::value
+                               ||   is_sameType::value
+                               ||   is_sameType::value;
+        };
+    }
+    
     template
-    struct Policy
+    struct Policy, 
+                         Duration>::type>
       {
         static function
         buildChangeHandler (Duration& target)
@@ -331,15 +342,15 @@ namespace time {
           }
       };
     
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (Duration& target)
-          {
-            return bind (Adap::template processValueChange, ref(target), _1 );
-          }
-      };
+//  template
+//  struct Policy
+//    {
+//      static function
+//      buildChangeHandler (Duration& target)
+//        {
+//          return bind (Adap::template processValueChange, ref(target), _1 );
+//        }
+//    };
     
     template
     struct Policy
@@ -573,9 +584,9 @@ namespace time {
     void
     Mutator::bind_to (TAR& target)  const
     {
-      setVal_ = Policy    ::buildChangeHandler (target);
+      setVal_ = Policy::buildChangeHandler (target);
       offset_ = Policy::buildChangeHandler (target);
-      nudge_  = Policy   ::buildChangeHandler (target);
+      nudge_  = Policy::buildChangeHandler (target);
     }
     
     template
diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp
index 978e8190f..f80dc9c6e 100644
--- a/src/lib/time/lumitime.cpp
+++ b/src/lib/time/lumitime.cpp
@@ -119,9 +119,16 @@ namespace time {
   }
   
   
-  /** duration of the given number of frames */
-  Duration::Duration (ulong count, FrameRate const& fps)
-    : TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL))
+  /** offset by the given number of frames. */
+  Offset::Offset (int64_t count, FrameRate const& fps)
+    : TimeValue (count?  (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
+                      : _raw(Duration::NIL))
+    { }
+  
+  /** duration of the given number of frames.
+   * @note always positive; count used absolute */
+  Duration::Duration (int64_t count, FrameRate const& fps)
+    : TimeValue (count? lumiera_framecount_to_time (abs(count), fps) : _raw(Duration::NIL))
     { }
   
   
diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp
index 5535beed1..0c8fb2e15 100644
--- a/src/lib/time/timevalue.hpp
+++ b/src/lib/time/timevalue.hpp
@@ -42,6 +42,11 @@ namespace time {
   
   namespace error = lumiera::error;
   
+  // forwards...
+  class FrameRate;
+  class TimeSpan;
+  class Mutation;
+  
   
   /**
    * basic constant internal time value.
@@ -193,8 +198,11 @@ namespace time {
         : TimeValue(TimeVar(target) -= origin)
         { }
       
+      Offset (int64_t count, FrameRate const& fps);
+      
       static const Offset ZERO;
       
+      
       TimeValue
       abs()  const
         {
@@ -243,7 +251,6 @@ namespace time {
    * @warning do not mix up gavl_time_t and FSecs */
   typedef boost::rational FSecs;
   
-  class FrameRate;
   
   /**
    * Lumiera's internal time value datatype.
@@ -306,8 +313,6 @@ namespace time {
   
   
   
-  class TimeSpan;
-  class Mutation;
   
   /**
    * Duration is the internal Lumiera time metric.
@@ -338,7 +343,7 @@ namespace time {
         { }
       
       Duration (TimeSpan const& interval);
-      Duration (ulong count, FrameRate const& fps);
+      Duration (int64_t count, FrameRate const& fps);
       
       static const Duration NIL;
       
diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index 6787e995c..db1fa6d83 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -140,14 +140,11 @@ namespace test{
           CHECK (c!=Time::ZERO && o != c, "unsuitable testdata");
           
           // 25fps-grid, but with an time origin offset by 1/50sec
-          TimeGrid::build("test_grid", FrameRate::PAL, Time(FSecs(1,50)));
+          TimeGrid::build("test_grid_PAL", FrameRate::PAL, Time(FSecs(1,50)));
           
           // disjoint NTSC-framerate grid for grid aligned changes
           TimeGrid::build("test_grid_NTSC", FrameRate::NTSC);
           
-          QuTime qChange (c, "test_grid");
-          FrameNr count(qChange);
-          
           verifyBasics();
           verifyMatrix_of_MutationCases(o,c);
         } 
@@ -210,7 +207,7 @@ namespace test{
         static QuTime
         build (TimeValue const& org)
           {
-            return QuTime (org, "test_grid");
+            return QuTime (org, "test_grid_PAL");
           }
       };
    
@@ -303,17 +300,17 @@ namespace test{
     void
     ____verify_nudged (TAR const& target, TAR const& refState, int64_t offsetSteps)
     {
-      CHECK (target != refState);
+      CHECK (target != refState  || !offsetSteps);
       CHECK (target == Time(refState)+Time(FSecs(offsetSteps)));
     }
     template<>
     void
     ____verify_nudged (QuTime const& target, QuTime const& refState, int64_t offsetSteps)
     {
-      CHECK (target != refState);
+      CHECK (target != refState  || !offsetSteps);
       PQuant quantiser(target);
       CHECK (target == Time(quantiser->materialise(refState))
-                     + offsetSteps * FrameRate::NTSC.duration());
+                     + Offset(offsetSteps, FrameRate::PAL));
     }
     
     
diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp
index e2316f240..27656bd44 100644
--- a/tests/lib/time/time-value-test.cpp
+++ b/tests/lib/time/time-value-test.cpp
@@ -233,7 +233,14 @@ namespace test{
           CHECK (9 == off9);
           // simple linear combinations
           CHECK (7 == -2*off9 + off5*5);
-        }
+          
+          // build offset by number of frames
+          Offset byFrames(-125, FrameRate::PAL);
+          CHECK (Time(FSecs(-5)) == byFrames);
+          
+          CHECK (Offset(-5, FrameRate(5,4)) == -Offset(5, FrameRate(5,4)));
+          CHECK (Offset(3, FrameRate(3)) == Offset(12345, FrameRate(24690,2)));
+        }                                // precise rational number calculations
       
       
       void

From a1427bb0b9790b7aa42049a110004f3d6191792c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 11 Jun 2011 21:10:47 +0200
Subject: [PATCH 033/296] next challenge: expected behaviour for quantised
 source/target values

---
 tests/lib/time/time-control-test.cpp | 77 ++++++++++++++++++++++++----
 1 file changed, 66 insertions(+), 11 deletions(-)

diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index db1fa6d83..a57c758f2 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -28,6 +28,7 @@
 #include "lib/time/control.hpp"
 #include "proc/asset/meta/time-grid.hpp"
 #include "lib/meta/generator-combinations.hpp"
+#include "lib/meta/util.hpp"
 #include "lib/scoped-holder.hpp"
 #include "lib/util.hpp"
 
@@ -181,6 +182,36 @@ namespace test{
   
   namespace { // Implementation: Matrix of individual test combinations
     
+    using lumiera::typelist::is_sameType;
+    
+    template
+    inline bool
+    isDuration()
+      {
+        return is_sameType::value;
+      }
+    
+    template
+    inline bool
+    isQuTime()
+      {
+        return is_sameType::value;
+      }
+    
+    template
+    inline TimeValue
+    materialise (T const&)
+      {
+        NOTREACHED ("only grid aligned values can be materialised");
+      }
+    inline TimeValue
+    materialise (QuTime const& alignedTime)
+      {
+        PQuant grid(alignedTime);
+        return grid->materialise (alignedTime);
+      }
+    
+    
     template
     struct TestTarget
       {
@@ -248,16 +279,23 @@ namespace test{
     void
     ____verify_wasChanged (TAR const& target, TimeValue const& org, SRC const& change)
     {
-      CHECK (target != org);
-      CHECK (target == change);
+      if (isDuration())
+        {
+          CHECK (target == org, "Logic error: Duration was changed by time value");
+        }
+      else
+      if (isQuTime())
+        {
+          CHECK (target != org);
+          CHECK (target == materialise(change));
+        }
+      else
+        {
+          CHECK (target != org);
+          CHECK (target == change);
+        }
     }
     
-    template
-    void
-    ____verify_wasChanged (Duration const& target, TimeValue const& org, SRC const&)
-    {
-      CHECK (target == org, "Logic error: Duration was changed by time value");
-    }
     void
     ____verify_wasChanged (Duration const& target, TimeValue const& org, Duration const& otherDuration)
     {
@@ -308,8 +346,8 @@ namespace test{
     ____verify_nudged (QuTime const& target, QuTime const& refState, int64_t offsetSteps)
     {
       CHECK (target != refState  || !offsetSteps);
-      PQuant quantiser(target);
-      CHECK (target == Time(quantiser->materialise(refState))
+      PQuant grid(target);
+      CHECK (target == Time (grid->materialise(refState))
                      + Offset(offsetSteps, FrameRate::PAL));
     }
     
@@ -318,7 +356,19 @@ namespace test{
     void
     ____verify_notification (TAR const& target, TestListener const& follower)
     {
-      CHECK (target == follower.receivedValue());
+      if (isDuration())
+        {
+          CHECK (Duration::NIL == follower.receivedValue());
+        }
+      else
+      if (isQuTime())
+        {
+          CHECK (materialise (target) == follower.receivedValue());
+        }
+      else
+        {
+          CHECK (target == follower.receivedValue());
+        }
     }
     void
     ____verify_notification (TimeSpan const& targetTimeSpan, TestListener const& follower)
@@ -326,6 +376,11 @@ namespace test{
       CHECK (follower.receivedValue() == targetTimeSpan.duration());
     }
     void
+    ____verify_notification (Duration const& target, TestListener const& follower)
+    {
+      CHECK (target == follower.receivedValue());
+    }
+    void
     ____verify_notification (Duration const& targetDuration, TestListener const& follower)
     {
       CHECK (Time::ZERO     == follower.receivedValue());

From fec2d25b52d596c12bfdcd884b85369ccf91ffa8 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 12 Jun 2011 01:11:15 +0200
Subject: [PATCH 034/296] additional quantisation and duration canges covered
 and passing test

---
 src/lib/time/control.hpp             | 51 ++++++++++++++++++++++++----
 tests/lib/time/time-control-test.cpp | 15 +++++---
 2 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index 48af1813c..60809e53d 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -200,13 +200,27 @@ namespace time {
         {
           return is_sameType::value;
         }
+      
+      template
+      inline T const&
+      maybeMaterialise (T const& non_grid_aligned_TimeValue)
+      {
+        return non_grid_aligned_TimeValue;
+      }
+#ifdef LIB_TIME_TIMEQUQNT_H
+      inline QuTime
+      maybeMaterialise (QuTime const& alignedTime)
+      {
+        PQuant const& grid(alignedTime);
+        return QuTime(grid->materialise(alignedTime), grid);
+      }
+#endif
     }
     
     
     template
     struct Builder
       {
-        
         static TI
         buildChangedValue (TAR const& target)
           {
@@ -235,7 +249,7 @@ namespace time {
     struct Builder
       {
         static TimeSpan
-        buildChangedValue (TimeSpan& target)
+        buildChangedValue (TimeSpan const& target)
           {
             return target;
           }
@@ -245,7 +259,7 @@ namespace time {
     struct Builder
       {
         static QuTime
-        buildChangedValue (TAR& target)
+        buildChangedValue (TAR const& target)
           {
             return QuTime (target
                           ,getDefaultGridFallback()                                                     //////////////////TICKET #810
@@ -256,7 +270,7 @@ namespace time {
     struct Builder
       {
         static QuTime
-        buildChangedValue (QuTime& target)
+        buildChangedValue (QuTime const& target)
           {
             return target;
           }
@@ -273,8 +287,8 @@ namespace time {
         static TI
         processValueChange (TAR& target, SRC const& change)
           {
-            imposeChange (target,change);
-            return buildChangedValue(target);
+            imposeChange (target, maybeMaterialise(change));
+            return buildChangedValue (maybeMaterialise(target));
           }
         
         static TI
@@ -329,6 +343,14 @@ namespace time {
                                ||   is_sameType::value
                                ||   is_sameType::value;
         };
+      
+      template
+      struct canReceiveDuration
+        {
+          static const bool value = is_sameType::value
+                               ||   is_sameType::value;
+        };
+      
     }
     
     template
@@ -342,6 +364,23 @@ namespace time {
           }
       };
     
+    template
+    struct Policy, 
+                     Duration>::type, TAR>
+      {
+        static function
+        buildChangeHandler (TAR&)
+          {
+            return bind ( ignore_change_and_return_Zero );
+          }
+        
+        static Duration
+        ignore_change_and_return_Zero()
+          {
+            return Duration::NIL;
+          }
+      };
+    
 //  template
 //  struct Policy
 //    {
diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index a57c758f2..ab12a2591 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -200,9 +200,9 @@ namespace test{
     
     template
     inline TimeValue
-    materialise (T const&)
+    materialise (T const& someTime)
       {
-        NOTREACHED ("only grid aligned values can be materialised");
+        return someTime;
       }
     inline TimeValue
     materialise (QuTime const& alignedTime)
@@ -284,6 +284,11 @@ namespace test{
           CHECK (target == org, "Logic error: Duration was changed by time value");
         }
       else
+      if (isDuration())
+        {
+          CHECK (target == org, "Logic error: Duration used to change time value");
+        }
+      else
       if (isQuTime())
         {
           CHECK (target != org);
@@ -346,8 +351,7 @@ namespace test{
     ____verify_nudged (QuTime const& target, QuTime const& refState, int64_t offsetSteps)
     {
       CHECK (target != refState  || !offsetSteps);
-      PQuant grid(target);
-      CHECK (target == Time (grid->materialise(refState))
+      CHECK (target == Time (materialise(refState))
                      + Offset(offsetSteps, FrameRate::PAL));
     }
     
@@ -358,7 +362,8 @@ namespace test{
     {
       if (isDuration())
         {
-          CHECK (Duration::NIL == follower.receivedValue());
+          CHECK (materialise(target) == follower.receivedValue()
+                 ||    Duration::NIL == follower.receivedValue() );
         }
       else
       if (isQuTime())

From f4d0d23e4800a3fd1932ce372871aecaf321fc0f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 12 Jun 2011 02:05:24 +0200
Subject: [PATCH 035/296] clean up leftovers from the first implementation
 attempts

---
 src/lib/time/control.hpp | 404 ++++++++-------------------------------
 1 file changed, 79 insertions(+), 325 deletions(-)

diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index 60809e53d..ba9fba5ce 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -62,27 +62,15 @@
 #include "lib/meta/util.hpp"
 #include "lib/time/mutation.hpp"
 #include "lib/time/timevalue.hpp"
-//#include "lib/symbol.hpp"
 
-//#include 
-//#include 
-//#include 
 #include 
 #include 
 #include 
-//#include 
 
 
 namespace lib {
 namespace time {
   
-//using lib::Symbol;
-//using std::string;
-//using lib::Literal;
-  
-  
-//LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated
-  
   namespace mutation {
     
     using boost::enable_if;
@@ -94,17 +82,23 @@ namespace time {
     using std::tr1::ref;
     
     
-    template
-    struct Mutabor;
-    
     
     /**
      * Implementation building block: impose changes to a Time element.
-     * The Mutator supports attaching a target time entity (through
-     * the Mutation interface), which then will be subject to any
-     * received value changes, offsets and grid nudging.
-     * 
-     * @todo WIP-WIP-WIP
+     * The purpose of the Mutator is to attach a target time entity,
+     * which then will be subject to any received value changes,
+     * offsets and grid nudging. The actual attachment is to be
+     * performed in a subclass, by using the Mutation interface.
+     * When attaching to a target, the Mutator will be outfitted 
+     * with a set of suitable functors, incorporating the specific
+     * behaviour for the concrete combination of input changes
+     * ("source values") and target object type. This works by
+     * binding to the appropriate implementation functionality,
+     * guided by a templated policy class. After installing
+     * these functors, these decisions remains opaque and
+     * encapsulated within the functor objects, so the
+     * mutator object doesn't need to carry this 
+     * type information on the interface
      */
     template
     class Mutator
@@ -114,18 +108,6 @@ namespace time {
         typedef function Ofsetter;
         typedef function           Nudger;
         
-        static TI imposeValueChange(TimeValue& target, TI const&);
-        static TI imposeOffset (TimeValue& target, Offset const&);
-        static TI imposeNudge (TimeValue& target, int);
-        
-        static TI changeDuration (Duration& target, TI const&);
-//      static TI nudgeDuration (Duration& target, int);
-        
-        template
-        struct MutationPolicy;
-        
-        friend class Mutabor;
-
       protected:
         mutable ValueSetter setVal_;
         mutable Ofsetter    offset_;
@@ -140,66 +122,84 @@ namespace time {
                                 ,error::LUMIERA_ERROR_UNCONNECTED);
           }
         
-      public:
-        // using default construction and copy
         
         template
         void bind_to (TAR& target)  const;
         
         void unbind();
         
+        // using default construction and copy
       };
     
+    
+    
+    /**
+     * Implementation building block: propagate changes to listeners.
+     * The Propagator manages a set of callback signals, allowing to
+     * propagate notifications for changed Time values.
+     * 
+     * There are no specific requirements on the acceptable listeners,
+     * besides exposing a function-call operator to feed the changed
+     * time value to. Both Mutator and Propagator employ one primary
+     * template parameter, which is the type of the time values
+     * to be fed in and propagated. 
+     */
     template
-    struct Mutabor
+    class Propagator
       {
+        typedef function ChangeSignal;
+        typedef std::vector ListenerList;
         
-        static void
-        imposeChange (TimeValue& target, TI const& newVal)
+        ListenerList listeners_;
+        
+      public:
+        /** install notification receiver */
+        template
+        void
+        attach (SIG const& toNotify)
           {
-            Mutator::imposeChange (target,newVal);
+            ChangeSignal newListener (ref(toNotify));
+            listeners_.push_back (newListener);
+          }
+        
+        /** disconnect any observers */
+        void
+        disconnnect()
+          {
+            listeners_.clear();
+          }
+        
+        /** publish a change */
+        TI
+        operator() (TI const& changedVal)  const
+          {
+            typedef typename ListenerList::const_iterator Iter;
+            Iter p = listeners_.begin();
+            Iter e = listeners_.end();
+            
+            for ( ; p!=e; ++p )
+              (*p) (changedVal);
+            return changedVal;
           }
       };
     
     
     
-    namespace { // metaprogramming helpers to pick the suitable Instantiation...
-      
-      template
-      struct isDurationZ
-        {
-          static const bool value = is_sameType::value;
-        };
-      template
-      struct isTimeSpanZ
-        {
-          static const bool value = is_sameType::value;
-        };
-      template
-      struct isQuTime
-        {
-          static const bool value = is_sameType::value;
-        };
-      
-      template
-      struct isSpecialCase
-        {
-          static const bool value = isDurationZ::value;
-        };
+    namespace { // metaprogramming helpers to pick suitable implementation branch...
       
       template
       inline bool
       isDuration()
-        {
-          return is_sameType::value;
-        }
+      {
+        return is_sameType::value;
+      }
       
       template
       inline bool
       isTimeSpan()
-        {
-          return is_sameType::value;
-        }
+      {
+        return is_sameType::value;
+      }
       
       template
       inline T const&
@@ -207,6 +207,7 @@ namespace time {
       {
         return non_grid_aligned_TimeValue;
       }
+      
 #ifdef LIB_TIME_TIMEQUQNT_H
       inline QuTime
       maybeMaterialise (QuTime const& alignedTime)
@@ -233,7 +234,7 @@ namespace time {
         static TimeSpan
         buildChangedValue (TAR const& target)
           {
-            return TimeSpan (target, Duration::NIL);  /////////////TODO how to feed the "new value" duration????
+            return TimeSpan (target, Duration::NIL);
           }
       };
     template<>
@@ -278,7 +279,7 @@ namespace time {
 #endif
     
     template
-    struct Adap
+    struct Link
       : Mutator
       , Builder
       {
@@ -328,7 +329,7 @@ namespace time {
         static function
         buildChangeHandler (TAR& target)
           {
-            return bind (Adap::template processValueChange, ref(target), _1 );
+            return bind (Link::template processValueChange, ref(target), _1 );
           }
       };
     
@@ -350,9 +351,9 @@ namespace time {
           static const bool value = is_sameType::value
                                ||   is_sameType::value;
         };
-      
     }
     
+    
     template
     struct Policy, 
                          Duration>::type>
@@ -360,7 +361,7 @@ namespace time {
         static function
         buildChangeHandler (Duration& target)
           {
-            return bind (Adap::dontChange, ref(target) );
+            return bind (Link::dontChange, ref(target) );
           }
       };
     
@@ -381,23 +382,13 @@ namespace time {
           }
       };
     
-//  template
-//  struct Policy
-//    {
-//      static function
-//      buildChangeHandler (Duration& target)
-//        {
-//          return bind (Adap::template processValueChange, ref(target), _1 );
-//        }
-//    };
-    
     template
     struct Policy
       {
         static function
         buildChangeHandler (Duration& target)
           {
-            return bind (Adap::useLengthAsChange, ref(target), _1 );
+            return bind (Link::useLengthAsChange, ref(target), _1 );
           }
       };
     
@@ -409,7 +400,7 @@ namespace time {
         static function
         buildChangeHandler (TimeSpan& target)
           {
-            return bind (Adap::mutateLength, ref(target), _1 );
+            return bind (Link::mutateLength, ref(target), _1 );
           }
       };
     
@@ -419,201 +410,11 @@ namespace time {
         static function
         buildChangeHandler (TimeSpan& target)
           {
-            return bind (Adap::mutateTimeSpan, ref(target), _1 );
+            return bind (Link::mutateTimeSpan, ref(target), _1 );
           }
       };
     
     
-/*    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Adap::imposeOffset, ref(target), _1 );
-          }
-      };
-    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Adap::imposeNudge, ref(target), _1 );
-          }
-      };
-*/    
-      
-    template
-    TI
-    Mutator::imposeValueChange (TimeValue& target, TI const& newVal)
-    {
-      return TI (Mutation::imposeChange (target,newVal));
-    }
-    template
-    TI
-    Mutator::imposeOffset (TimeValue& target, Offset const& off)
-    {
-      return TI (Mutation::imposeChange (target, TimeVar(target)+off));
-    }
-    template
-    TI
-    Mutator::imposeNudge (TimeValue& target, int off_by_steps)
-    {
-      return TI (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps))));     //////////////////TICKET #810
-    }
-    
-    // special cases...
-    template
-    TI
-    Mutator::changeDuration (Duration&, TI const&)
-    {
-      return TI (Time::ZERO);
-    }
-    template<>
-    Duration
-    Mutator::changeDuration (Duration& target, Duration const& changedDur)
-    {
-      return Duration (Mutation::imposeChange (target,changedDur));
-    }
-    template<>
-    TimeSpan
-    Mutator::changeDuration (Duration& target, TimeSpan const& timeSpan_to_impose)
-    {
-      return TimeSpan (timeSpan_to_impose.start()
-                      ,Duration(Mutation::imposeChange (target,timeSpan_to_impose.duration()))
-                      );
-    }
-    
-    template<>
-    TimeSpan
-    Mutator::imposeValueChange (TimeValue& target, TimeSpan const& newVal)
-    {
-      Mutation::imposeChange (target,newVal);
-      return newVal;
-    }
-    template<>
-    TimeSpan
-    Mutator::imposeOffset (TimeValue& target, Offset const& off)
-    {
-      return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+off), Duration::NIL);
-    }
-    template<>
-    TimeSpan
-    Mutator::imposeNudge (TimeValue& target, int off_by_steps)
-    {
-      return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps))), Duration::NIL);
-    }
-    
-
-#ifdef LIB_TIME_TIMEQUQNT_H
-    template<>
-    QuTime
-    Mutator::changeDuration (Duration&, QuTime const& irrelevantChange)
-    {
-      return QuTime (Time::ZERO, PQuant(irrelevantChange));
-    }
-    template<>
-    QuTime
-    Mutator::imposeValueChange (TimeValue& target, QuTime const& grid_aligned_Value_to_set)
-    {
-      PQuant quantiser (grid_aligned_Value_to_set);
-      TimeValue appliedChange = quantiser->materialise(grid_aligned_Value_to_set);
-      Mutation::imposeChange (target, appliedChange);
-      return QuTime (target, quantiser);
-    }
-    template<>
-    QuTime
-    Mutator::imposeOffset (TimeValue& target, Offset const& off)
-    {
-      return QuTime (Mutation::imposeChange (target, TimeVar(target)+off)
-                    ,getDefaultGridFallback()                                                     //////////////////TICKET #810
-                    );
-    }
-    template<>
-    QuTime
-    Mutator::imposeNudge (TimeValue& target, int off_by_steps)
-    {
-      return QuTime (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps)))
-                    ,getDefaultGridFallback()                                                     //////////////////TICKET #810
-                    );
-    }
-#endif //(End)quantisation special case    
-    
-    template
-    template
-    struct Mutator::MutationPolicy<                  typename disable_if< isSpecialCase, 
-                                       TI>::type, TAR>
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Mutator::imposeValueChange, ref(target), _1 );
-          }
-      };
-    
-    template
-    template
-    struct Mutator::MutationPolicy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Mutator::imposeOffset, ref(target), _1 );
-          }
-      };
-    
-    template
-    template
-    struct Mutator::MutationPolicy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Mutator::imposeNudge, ref(target), _1 );
-          }
-      };
-    
-    
-    //special cases for changing Durations....
-
-    template
-    template
-    struct Mutator::MutationPolicy<                  typename enable_if< isDurationZ, 
-                                       TI>::type, TAR>
-      {
-        static function
-        buildChangeHandler (Duration& target)
-          {
-            return bind (Mutator::changeDuration, ref(target), _1 );
-          }
-      };
-    
-    //    
-//    template
-//    template
-//    struct Mutator::MutationPolicy
-//      {
-//        static function
-//        buildChangeHandler (Duration& target)
-//          {
-//            return bind (Mutator::changeDuration, ref(target), _1 );
-//          }
-//      };
-    
-//    template
-//    template<>
-//    struct Mutator::MutationPolicy
-//      {
-//        static function
-//        buildChangeHandler (Duration& target)
-//          {
-//            return bind (Mutator::nudgeDuration, ref(target), _1 );
-//          }
-//      };
-    
     
     
     
@@ -637,59 +438,12 @@ namespace time {
       nudge_  = Nudger();
     }
     
-    
-    
-    /**
-     * Implementation building block: propagate changes to listeners.
-     * The Propagator manages a set of callback signals, allowing to
-     * propagate notifications for changed Time values.
-     * 
-     * @todo WIP-WIP-WIP
-     */
-    template
-    class Propagator
-      {
-        typedef function ChangeSignal;
-        typedef std::vector ListenerList;
-        
-        ListenerList listeners_;
-        
-      public:
-        /** install notification receiver */
-        template
-        void
-        attach (SIG const& toNotify)
-          {
-            ChangeSignal newListener (ref(toNotify));
-            listeners_.push_back (newListener);
-          }
-        
-        /** disconnect any observers */
-        void
-        disconnnect()
-          {
-            listeners_.clear();
-          }
-        
-        /** publish a change */
-        TI
-        operator() (TI const& changedVal)  const
-          {
-            typedef typename ListenerList::const_iterator Iter;
-            Iter p = listeners_.begin();
-            Iter e = listeners_.end();
-            
-            for ( ; p!=e; ++p )
-              (*p) (changedVal);
-            return changedVal;
-          }
-      };
   }
   
   
   /**
-   * Frontend/Interface: controller-element for retrieving and
-   * changing running time values
+   * Frontend/Interface: controller-element to retrieve
+   * and change running time values
    * 
    * @see time::Mutation
    * @see time::TimeSpan#accept(Mutation const&)
@@ -725,7 +479,7 @@ namespace time {
   
   
   
-  /* === implementation === */
+  /* === forward to implementation === */
   
   template
   void

From 2099ecbcac6d09a58ed19753db6a7b29cd929b23 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 12 Jun 2011 19:03:12 +0200
Subject: [PATCH 036/296] finish and comment the new time::Control facility

---
 src/lib/time/control-impl.hpp        | 203 ++++++++++++
 src/lib/time/control-policy.hpp      | 400 +++++++++++++++++++++++
 src/lib/time/control.hpp             | 467 +++++----------------------
 tests/40components.tests             |  17 +-
 tests/lib/time/time-control-test.cpp |  70 ++--
 5 files changed, 747 insertions(+), 410 deletions(-)
 create mode 100644 src/lib/time/control-impl.hpp
 create mode 100644 src/lib/time/control-policy.hpp

diff --git a/src/lib/time/control-impl.hpp b/src/lib/time/control-impl.hpp
new file mode 100644
index 000000000..1d2ab6d94
--- /dev/null
+++ b/src/lib/time/control-impl.hpp
@@ -0,0 +1,203 @@
+/*
+  CONTROL-IMPL.hpp  -  time::control implementation building blocks  
+
+  Copyright (C)         Lumiera.org
+    2011,               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 control-impl.hpp
+ ** Implementation building blocks for time modification and propagation.
+ ** The time::Control element allows to impose modifications to a connected
+ ** time value entity and at the same time publish the changes to registered
+ ** listeners. Due to the various flavours of actual time value entities, this
+ ** is a complex undertaking, which is implemented here based on policies and
+ ** template metaprogramming. This header/include defines two building blocks:
+ ** - the actual Mutator to apply the changes to the target entity
+ ** - a Propagator to register listeners and forward the changes.
+ ** 
+ ** \par implementation technique
+ ** 
+ ** The Mutator uses functor objects to encapsulate the actual modification
+ ** operations. When attaching to a target time entity to be manipulated, these
+ ** functor objects will be configured by binding them to the appropriate
+ ** implementation function. And picking this actual implementation is done
+ ** through a time::mutation::Policy element, using the concrete time entity
+ ** types as template parameter. Thus, the actual implementation to be used
+ ** is determined by the compiler, through the template specialisations
+ ** contained in control-policy.hpp
+ ** 
+ ** @note the header control-policy.hpp with the template specialisations
+ **       is included way down, after the class definitions. This is done
+ **       so for sake of readability
+ ** 
+ ** @see TimeControl_test
+ **
+ */
+
+#ifndef LIB_TIME_CONTROL_IMPL_H
+#define LIB_TIME_CONTROL_IMPL_H
+
+#include "lib/error.hpp"
+#include "lib/time/mutation.hpp"
+#include "lib/time/timevalue.hpp"
+
+#include 
+
+
+namespace lib {
+namespace time {
+namespace mutation {
+  
+  
+  
+  
+  /**
+   * Implementation building block: impose changes to a Time element.
+   * The purpose of the Mutator is to attach a target time entity,
+   * which then will be subject to any received value changes,
+   * offsets and grid nudging. The actual attachment is to be
+   * performed in a subclass, by using the Mutation interface.
+   * When attaching to a target, the Mutator will be outfitted 
+   * with a set of suitable functors, incorporating the specific
+   * behaviour for the concrete combination of input changes
+   * ("source values") and target object type. This works by
+   * binding to the appropriate implementation functionality,
+   * guided by a templated policy class. After installing
+   * these functors, these decisions remains opaque and
+   * encapsulated within the functor objects, so the
+   * mutator object doesn't need to carry this 
+   * type information on the interface
+   */
+  template
+  class Mutator
+    : public Mutation
+    {
+      typedef function     ValueSetter;
+      typedef function Ofsetter;
+      typedef function           Nudger;
+      
+    protected:
+      mutable ValueSetter setVal_;
+      mutable Ofsetter    offset_;
+      mutable Nudger      nudge_;
+      
+      void
+      ensure_isArmed()  const
+        {
+          if (!setVal_)
+            throw error::State("feeding time/value change "
+                               "while not (yet) connected to any target to change"
+                              ,error::LUMIERA_ERROR_UNCONNECTED);
+        }
+      
+      
+      template
+      void bind_to (TAR& target)  const;
+      
+      void unbind();
+      
+      // using default construction and copy
+    };
+  
+  
+  
+  /**
+   * Implementation building block: propagate changes to listeners.
+   * The Propagator manages a set of callback signals, allowing to
+   * propagate notifications for changed Time values.
+   * 
+   * There are no specific requirements on the acceptable listeners,
+   * besides exposing a function-call operator to feed the changed
+   * time value to. Both Mutator and Propagator employ one primary
+   * template parameter, which is the type of the time values
+   * to be fed in and propagated. 
+   */
+  template
+  class Propagator
+    {
+      typedef function ChangeSignal;
+      typedef std::vector ListenerList;
+      
+      ListenerList listeners_;
+      
+    public:
+      /** install notification receiver */
+      template
+      void
+      attach (SIG const& toNotify)
+        {
+          ChangeSignal newListener (ref(toNotify));
+          listeners_.push_back (newListener);
+        }
+      
+      /** disconnect any observers */
+      void
+      disconnnect()
+        {
+          listeners_.clear();
+        }
+      
+      /** publish a change */
+      TI
+      operator() (TI const& changedVal)  const
+        {
+          typedef typename ListenerList::const_iterator Iter;
+          Iter p = listeners_.begin();
+          Iter e = listeners_.end();
+          
+          for ( ; p!=e; ++p )
+            (*p) (changedVal);
+          return changedVal;
+        }
+      
+      // using default construction and copy
+    };
+  
+}}} // lib::time::mutation
+
+
+/* ===== Definition of actual operations ===== */
+#include "lib/time/control-policy.hpp"
+
+
+
+
+template
+template
+void
+lib::time::mutation::Mutator::bind_to (TAR& target)  const
+{
+  using lib::time::mutation::Policy;
+  
+  setVal_ = Policy::buildChangeHandler (target);
+  offset_ = Policy::buildChangeHandler (target);
+  nudge_  = Policy::buildChangeHandler (target);
+}
+
+template
+void
+lib::time::mutation::Mutator::unbind()
+{
+  setVal_ = ValueSetter();
+  offset_ = Ofsetter();
+  nudge_  = Nudger();
+}
+
+
+#endif
diff --git a/src/lib/time/control-policy.hpp b/src/lib/time/control-policy.hpp
new file mode 100644
index 000000000..a941c3c96
--- /dev/null
+++ b/src/lib/time/control-policy.hpp
@@ -0,0 +1,400 @@
+/*
+  CONTROL-POLICY.hpp  -  detail definition of actual time changing functionality  
+
+  Copyright (C)         Lumiera.org
+    2011,               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 control-policy.hpp
+ ** Definition of special cases when imposing a change onto concrete time values.
+ ** The time::Control element allows to impose modifications to a connected
+ ** time value entity and at the same time publish the changes to registered
+ ** listeners. Due to the various flavours of actual time value entities, this
+ ** is a complex undertaking, which is implemented here based on policies and
+ ** template metaprogramming.
+ ** 
+ ** The header control-impl.hpp defines the building blocks for time::Control
+ ** and then includes this header here to get the concrete template specialisations
+ ** for time::mutation::Policy. This policy class is templated by time entity types
+ ** - for \c TI, the \em nominal value type used on the time::Control interface
+ ** - for \c SRC, the actual type of values to impose as \em change
+ ** - for \c TAR, the target time value's type, receiving those changes.
+ ** 
+ ** \par mutating a time value entity
+ ** 
+ ** Actually imposing a change to the attached time value entity involves several
+ ** steps. Each of these steps might be adapted specifically, in accordance to
+ ** the concrete time value types involved.
+ ** - TimeValue, Time
+ ** - Offset
+ ** - Duration
+ ** - TimeSpan
+ ** - QuTime (grid aligned time value)
+ ** - QuTimeSpan (planned as of 6/2011)
+ ** 
+ ** Moreover, the combination of types needs to be taken into account. For example,
+ ** it doesn't make sense to apply a Duration value as change to a TimeValue, which
+ ** has no duration (temporal extension). While a TimeSpan might receive a Duration
+ ** change, but behaves differently when imposing a Time to manipulate the starting
+ ** point of the time interval given by the TimeSpan.
+ ** 
+ ** Incoming changes might be of any of the aforementioned types, and in addition,
+ ** we might receive \em nudging, which means to increment or decrement the target
+ ** time value in discrete steps. After maybe adapting these incoming change values,
+ ** they may be actually \em imposed to the target. In all cases, this is delegated
+ ** to the time::Mutation base class, which is declared fried to TimeValue and thus
+ ** has the exceptional ability to manipulate time values, which otherwise are defined
+ ** to be immutable. Additionally, these protected functions in the time::Mutation
+ ** baseclass also know how to handle \em nudge values, either by using the native
+ ** (embedded) time grid of a quantised time value, or by falling back to a standard
+ ** nudging grid, defined in the session context (TODO as of 6/2011).                     //////////////////////TICKET #810
+ ** 
+ ** After (maybe) imposing a change to the target, the change \em notification value
+ ** needs to be built. This is the time value entity to be forwarded to registered
+ ** listeners. This notification value has to be given as the type \c TI, in accordance
+ ** to the \c time::Control frontend definition used in the concrete usage situation.
+ ** As this type \c TI might be different to the actual target type, and again different
+ ** to the type of the change handed in, in some cases this involves a second conversion
+ ** step, to represent the current state of the target \c TAR in terms of the interface
+ ** type \c TI.
+ ** 
+ ** \par changing quantised (grid aligned) time entities
+ ** 
+ ** The time::Control element includes the capability to handle grid aligned time values,
+ ** both as target and as change/notification value. This ability is compiled in conditionally,
+ ** as including mutation.hpp causes several additional includes, which isn't desirable when
+ ** it comes just to changing plain time values. Thus, to get these additional specialisations,
+ ** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically
+ ** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
+ ** 
+ ** As a special convention, any \em quantised (grid aligned) types involved in these
+ ** time changes will be \em materialised, whenever a type conversion happens. Generally
+ ** speaking, a quantised time value contains an (opaque) raw time value, plus a reference
+ ** to a time grid definition to apply. In this context \em materialising means actually
+ ** to apply this time grid to yield a grid aligned value. Thus, when using a quantised
+ ** value to impose as change (or to receive a change), its grid aligning nature
+ ** becomes effective, by applying the \em current definition of the grid to
+ ** create a fixed (materialised) time value, aligned to that current grid.
+ ** 
+ ** @todo 6/2011 include all the special cases for QuTimeSpan                               ////////////////////TICKET #760
+ ** 
+ ** @see TimeControl_test
+ **
+ */
+
+#ifndef LIB_TIME_CONTROL_POLICY_H
+#define LIB_TIME_CONTROL_POLICY_H
+
+#include "lib/meta/util.hpp"
+#include "lib/time/mutation.hpp"
+#include "lib/time/timevalue.hpp"
+
+#include 
+#include 
+
+
+namespace lib {
+namespace time {
+  
+namespace mutation {
+  
+  using boost::disable_if;
+  using lumiera::typelist::is_sameType;
+  using std::tr1::placeholders::_1;
+  using std::tr1::function;
+  using std::tr1::bind;
+  using std::tr1::ref;
+  
+  
+  
+  namespace { // metaprogramming helpers to pick a suitable implementation branch...
+    
+    template
+    inline bool
+    isDuration()
+    {
+      return is_sameType::value;
+    }
+    
+    template
+    inline bool
+    isTimeSpan()
+    {
+      return is_sameType::value;
+    }
+    
+    template
+    inline T const&
+    maybeMaterialise (T const& non_grid_aligned_TimeValue)
+    {
+      return non_grid_aligned_TimeValue;
+    }
+    
+#ifdef LIB_TIME_TIMEQUQNT_H
+    inline QuTime
+    maybeMaterialise (QuTime const& alignedTime)
+    {
+      PQuant const& grid(alignedTime);
+      return QuTime(grid->materialise(alignedTime), grid);
+    }
+#endif //--quantised-time-support
+  }
+  
+  
+  /** 
+   * Implementation policy: how to build a new
+   * notification value of type \c TI, given a 
+   * target time value entity of type \c TAR
+   */
+  template
+  struct Builder
+    {
+      static TI
+      buildChangedValue (TAR const& target)
+        {
+          return TI(target);
+        }
+    };
+  template
+  struct Builder
+    {
+      static TimeSpan
+      buildChangedValue (TAR const& target)
+        {
+          return TimeSpan (target, Duration::NIL);
+        }
+    };
+  template<>
+  struct Builder
+    {
+      static TimeSpan
+      buildChangedValue (Duration const& targetDuration)
+        {
+          return TimeSpan (Time::ZERO, targetDuration);
+        }
+    };
+  template<>
+  struct Builder
+    {
+      static TimeSpan
+      buildChangedValue (TimeSpan const& target)
+        {
+          return target;
+        }
+    };
+#ifdef LIB_TIME_TIMEQUQNT_H
+  template
+  struct Builder
+    {
+      static QuTime
+      buildChangedValue (TAR const& target)
+        {
+          return QuTime (target
+                        ,getDefaultGridFallback()                                                     //////////////////TICKET #810
+                        );
+        }
+    };
+  template<>
+  struct Builder
+    {
+      static QuTime
+      buildChangedValue (QuTime const& target)
+        {
+          return target;
+        }
+    };
+#endif //--quantised-time-support
+  
+  
+  
+  
+  /**  
+   * Policy to tie the various detail policies together
+   * for providing actual value change operations.
+   * The standard case uses the (inherited) time::Mutation
+   * base implementation to impose a new value onto the
+   * target entity and then uses the Builder policy to
+   * create a notification value reflecting this change.
+   */
+  template
+  struct Link
+    : Mutator
+    , Builder
+    {
+      
+      template
+      static TI
+      processValueChange (TAR& target, SRC const& change)  ///< standard case: plain value change
+        {
+          imposeChange (target, maybeMaterialise(change));
+          return buildChangedValue (maybeMaterialise(target));
+        }
+      
+      static TI
+      useLengthAsChange (TAR& target, TimeSpan const& change)
+        {
+          return processValueChange(target, change.duration());
+        }
+      
+      static TI
+      mutateLength (TimeSpan& target, Duration const& change)
+        {
+          Mutator::imposeChange (target.duration(), change);
+          return Builder::buildChangedValue(target);
+        }
+      
+      static TimeSpan
+      mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
+        {
+          Mutator::imposeChange (target.duration(), change.duration());
+          Mutator::imposeChange (target,change.start());
+          return Builder::buildChangedValue(target);
+        }
+      
+      static TI
+      dontChange (TAR& target)  ///< @note: not touching the target
+        {
+          return buildChangedValue(target);
+        }
+    };
+  
+  
+  
+  /** 
+   * Policy how to impose changes onto a connected target time value entity
+   * This policy will be parametrised with the concrete time entity types
+   * involved in the usage situation of time::Control. The purpose of the
+   * policy is to \em bind a functor object to the concrete implementation
+   * of the value change applicable for this combination of types.
+   * This functor will then be stored within time::Control and
+   * invoked for each actual value change.
+   * @param TI the nominal (interface) type of the change, propagated to listeners
+   * @param SRC the actual type of the change to be imposed
+   * @param TAR the actual type of the target entity to receive the changes
+   * @note typically either SRC is identical to TI, or it is an
+   *       time::Offset, or an int for \em nudging the target 
+   */
+  template
+  struct Policy
+    {
+      static function
+      buildChangeHandler (TAR& target)
+        {
+          return bind (Link::template processValueChange, ref(target), _1 );
+        }
+    };
+  
+  
+  // special treatment of Durations as target------------------------------------
+  
+  namespace {
+    template
+    struct canMutateDuration
+      {
+        static const bool value = is_sameType::value
+                             ||   is_sameType::value
+                             ||   is_sameType::value;
+      };
+    
+    template
+    struct canReceiveDuration
+      {
+        static const bool value = is_sameType::value
+                             ||   is_sameType::value;
+      };
+  }
+  
+  
+  /** 
+   * special case: a Duration target value can't be changed by plain time values.
+   * This specialisation is \em not used (\c disable_if ) when the given change (SRC)
+   * is applicable to a Duration in a sensible way. We either define explicit
+   * specialisations (for TimeSpan) or fall back to the default in these cases. 
+   */
+  template
+  struct Policy, 
+                       Duration>::type>
+    {
+      static function
+      buildChangeHandler (Duration& target)
+        {
+          return bind (Link::dontChange, ref(target) );
+        }
+    };
+  
+  /**
+   * special case: a Duration change value can't be imposed to a plain time value.
+   * In these cases, we even propagate a Duration::ZERO to the listeners.
+   * As above, there are exceptions to this behaviour, where a Duration change
+   * can sensibly be applied. 
+   */
+  template
+  struct Policy, 
+                   Duration>::type, TAR>
+    {
+      static function
+      buildChangeHandler (TAR&)
+        {
+          return bind ( ignore_change_and_return_Zero );
+        }
+      
+      static Duration
+      ignore_change_and_return_Zero()
+        {
+          return Duration::NIL;
+        }
+    };
+  
+  template
+  struct Policy
+    {
+      static function
+      buildChangeHandler (Duration& target)
+        {
+          return bind (Link::useLengthAsChange, ref(target), _1 );
+        }
+    };
+  
+  
+  // special treatment for TimeSpan values---------------------------------------
+  
+  template
+  struct Policy
+    {
+      static function
+      buildChangeHandler (TimeSpan& target)
+        {
+          return bind (Link::mutateLength, ref(target), _1 );
+        }
+    };
+  
+  template<>
+  struct Policy
+    {
+      static function
+      buildChangeHandler (TimeSpan& target)
+        {
+          return bind (Link::mutateTimeSpan, ref(target), _1 );
+        }
+    };
+  
+  
+  
+}}} // namespace lib::time::mutation
+#endif
diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index ba9fba5ce..cbac65d33 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -40,6 +40,46 @@
  ** of time-like entities -- be it the running time display in a GUI widget, a ruler marker
  ** which can be dragged, a modifiable selection or the animated playhead cursor.
  ** 
+ ** \par usage scenarios
+ ** The time::Control element provides mediating functionality, but doesn't assume or provide
+ ** anything special regarding the usage pattern or the lifecycle, beyond the ability to
+ ** attach listeners, attach to a (different) target and to detach from all connections.
+ ** Especially, no assumptions are made about which side is the server or the client
+ ** and who owns the time::Control element.
+ ** 
+ ** Thus an interface might accept a time::Control element \em reference (e.g. the
+ ** lumiera::Play::Controller uses this pattern) -- meaning that the client owns the
+ ** Control element and might attach listeners, while the implementation (server side)
+ ** will attach the Control to mutate an time value entity otherwise not disclosed
+ ** (e.g. the playhead position of the playback process). Of course, in this case
+ ** the client is responsible for keeping the Control element and all listeners
+ ** alive, and to invoke Control#disconnect prior to destroying the element.
+ ** 
+ ** Of course, the reversed usage situation would be possible as well: an interface
+ ** exposing a time::Control, thus allowing to attach target and listeners, while the
+ ** actual changes will originate somewhere within the service implementation.
+ ** 
+ ** Another usage pattern would be to expose a time::Control \c const&, allowing only to
+ ** impose changes, but not to change the target or listener attachments. To the contrary,
+ ** when exposing only a time::Mutation \c const& through an interface allows only to
+ ** attach new target elements, but not to change listeners or feed any value changes.
+ ** 
+ ** Using time::Control as an implementation building block and just exposing the
+ ** change (function) operators or the listener attachment through an forwarding sub
+ ** interface is another option.
+ ** 
+ ** @note time::Control is default constructible and freely copyable.
+ ** 
+ ** 
+ ** \par changing quantised (grid aligned) time entities
+ ** 
+ ** The time::Control element includes the functionality to handle grid aligned time values,
+ ** both as target and as change/notification value. This ability is compiled in conditionally,
+ ** as including mutation.hpp causes several additional includes, which isn't desirable when
+ ** it comes just to changing plain time values. Thus, to get these additional specialisations,
+ ** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically
+ ** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
+ ** 
  ** \par implementation notes
  ** - the validity of a given combination of change and target is checked immediately,
  **   when connecting to the target. Depending on the situation, the actual changes later
@@ -48,406 +88,42 @@
  **   processed within its own call context (function invocation), parallelism is only
  **   a concern with respect to the value finally visible within the target.
  ** - the change notification is processed right away, after applying the change to the
- **   target; in all cases, the effective change value is what will be propagated, \em not
- **   the content of the target after applying the change
+ **   target; of course there is a race between applying the value and building the
+ **   response value passed on as notification. In all cases, the effective change
+ **   notification value is built from the state of the target after applying
+ **   the change, which might or might not reflect the change value passed in.
  ** 
- ** @todo WIP-WIP-WIP
+ ** @todo include support for QuTimeSpan values                               ////////////////////TICKET #760
  **
  */
 
 #ifndef LIB_TIME_CONTROL_H
 #define LIB_TIME_CONTROL_H
 
-#include "lib/error.hpp"
-#include "lib/meta/util.hpp"
 #include "lib/time/mutation.hpp"
 #include "lib/time/timevalue.hpp"
+#include "lib/time/control-impl.hpp"
 
-#include 
-#include 
-#include 
 
 
 namespace lib {
 namespace time {
   
-  namespace mutation {
-    
-    using boost::enable_if;
-    using boost::disable_if;
-    using lumiera::typelist::is_sameType;
-    using std::tr1::placeholders::_1;
-    using std::tr1::function;
-    using std::tr1::bind;
-    using std::tr1::ref;
-    
-    
-    
-    /**
-     * Implementation building block: impose changes to a Time element.
-     * The purpose of the Mutator is to attach a target time entity,
-     * which then will be subject to any received value changes,
-     * offsets and grid nudging. The actual attachment is to be
-     * performed in a subclass, by using the Mutation interface.
-     * When attaching to a target, the Mutator will be outfitted 
-     * with a set of suitable functors, incorporating the specific
-     * behaviour for the concrete combination of input changes
-     * ("source values") and target object type. This works by
-     * binding to the appropriate implementation functionality,
-     * guided by a templated policy class. After installing
-     * these functors, these decisions remains opaque and
-     * encapsulated within the functor objects, so the
-     * mutator object doesn't need to carry this 
-     * type information on the interface
-     */
-    template
-    class Mutator
-      : public Mutation
-      {
-        typedef function     ValueSetter;
-        typedef function Ofsetter;
-        typedef function           Nudger;
-        
-      protected:
-        mutable ValueSetter setVal_;
-        mutable Ofsetter    offset_;
-        mutable Nudger      nudge_;
-        
-        void
-        ensure_isArmed()
-          {
-            if (!setVal_)
-              throw error::State("feeding time/value change "
-                                 "while not (yet) connected to any target to change"
-                                ,error::LUMIERA_ERROR_UNCONNECTED);
-          }
-        
-        
-        template
-        void bind_to (TAR& target)  const;
-        
-        void unbind();
-        
-        // using default construction and copy
-      };
-    
-    
-    
-    /**
-     * Implementation building block: propagate changes to listeners.
-     * The Propagator manages a set of callback signals, allowing to
-     * propagate notifications for changed Time values.
-     * 
-     * There are no specific requirements on the acceptable listeners,
-     * besides exposing a function-call operator to feed the changed
-     * time value to. Both Mutator and Propagator employ one primary
-     * template parameter, which is the type of the time values
-     * to be fed in and propagated. 
-     */
-    template
-    class Propagator
-      {
-        typedef function ChangeSignal;
-        typedef std::vector ListenerList;
-        
-        ListenerList listeners_;
-        
-      public:
-        /** install notification receiver */
-        template
-        void
-        attach (SIG const& toNotify)
-          {
-            ChangeSignal newListener (ref(toNotify));
-            listeners_.push_back (newListener);
-          }
-        
-        /** disconnect any observers */
-        void
-        disconnnect()
-          {
-            listeners_.clear();
-          }
-        
-        /** publish a change */
-        TI
-        operator() (TI const& changedVal)  const
-          {
-            typedef typename ListenerList::const_iterator Iter;
-            Iter p = listeners_.begin();
-            Iter e = listeners_.end();
-            
-            for ( ; p!=e; ++p )
-              (*p) (changedVal);
-            return changedVal;
-          }
-      };
-    
-    
-    
-    namespace { // metaprogramming helpers to pick suitable implementation branch...
-      
-      template
-      inline bool
-      isDuration()
-      {
-        return is_sameType::value;
-      }
-      
-      template
-      inline bool
-      isTimeSpan()
-      {
-        return is_sameType::value;
-      }
-      
-      template
-      inline T const&
-      maybeMaterialise (T const& non_grid_aligned_TimeValue)
-      {
-        return non_grid_aligned_TimeValue;
-      }
-      
-#ifdef LIB_TIME_TIMEQUQNT_H
-      inline QuTime
-      maybeMaterialise (QuTime const& alignedTime)
-      {
-        PQuant const& grid(alignedTime);
-        return QuTime(grid->materialise(alignedTime), grid);
-      }
-#endif
-    }
-    
-    
-    template
-    struct Builder
-      {
-        static TI
-        buildChangedValue (TAR const& target)
-          {
-            return TI(target);
-          }
-      };
-    template
-    struct Builder
-      {
-        static TimeSpan
-        buildChangedValue (TAR const& target)
-          {
-            return TimeSpan (target, Duration::NIL);
-          }
-      };
-    template<>
-    struct Builder
-      {
-        static TimeSpan
-        buildChangedValue (Duration const& targetDuration)
-          {
-            return TimeSpan (Time::ZERO, targetDuration);
-          }
-      };
-    template<>
-    struct Builder
-      {
-        static TimeSpan
-        buildChangedValue (TimeSpan const& target)
-          {
-            return target;
-          }
-      };
-#ifdef LIB_TIME_TIMEQUQNT_H
-    template
-    struct Builder
-      {
-        static QuTime
-        buildChangedValue (TAR const& target)
-          {
-            return QuTime (target
-                          ,getDefaultGridFallback()                                                     //////////////////TICKET #810
-                          );
-          }
-      };
-    template<>
-    struct Builder
-      {
-        static QuTime
-        buildChangedValue (QuTime const& target)
-          {
-            return target;
-          }
-      };
-#endif
-    
-    template
-    struct Link
-      : Mutator
-      , Builder
-      {
-        
-        template
-        static TI
-        processValueChange (TAR& target, SRC const& change)
-          {
-            imposeChange (target, maybeMaterialise(change));
-            return buildChangedValue (maybeMaterialise(target));
-          }
-        
-        static TI
-        useLengthAsChange (TAR& target, TimeSpan const& change)
-          {
-            return processValueChange(target, change.duration());
-          }
-        
-        static TI
-        mutateLength (TimeSpan& target, Duration const& change)
-          {
-            Mutator::imposeChange (target.duration(), change);
-            return Builder::buildChangedValue(target);
-          }
-        
-        static TimeSpan
-        mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
-          {
-            Mutator::imposeChange (target.duration(), change.duration());
-            Mutator::imposeChange (target,change.start());
-            return Builder::buildChangedValue(target);
-          }
-        
-        static TI
-        dontChange (TAR& target)
-          {
-            // note: not touching the target
-            return buildChangedValue(target);
-          }
-      };
-    
-    
-    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Link::template processValueChange, ref(target), _1 );
-          }
-      };
-    
-    
-    // special treatment of Durations as target...
-    
-    namespace {
-      template
-      struct canMutateDuration
-        {
-          static const bool value = is_sameType::value
-                               ||   is_sameType::value
-                               ||   is_sameType::value;
-        };
-      
-      template
-      struct canReceiveDuration
-        {
-          static const bool value = is_sameType::value
-                               ||   is_sameType::value;
-        };
-    }
-    
-    
-    template
-    struct Policy, 
-                         Duration>::type>
-      {
-        static function
-        buildChangeHandler (Duration& target)
-          {
-            return bind (Link::dontChange, ref(target) );
-          }
-      };
-    
-    template
-    struct Policy, 
-                     Duration>::type, TAR>
-      {
-        static function
-        buildChangeHandler (TAR&)
-          {
-            return bind ( ignore_change_and_return_Zero );
-          }
-        
-        static Duration
-        ignore_change_and_return_Zero()
-          {
-            return Duration::NIL;
-          }
-      };
-    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (Duration& target)
-          {
-            return bind (Link::useLengthAsChange, ref(target), _1 );
-          }
-      };
-    
-    // special treatment for TimeSpan values...
-    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TimeSpan& target)
-          {
-            return bind (Link::mutateLength, ref(target), _1 );
-          }
-      };
-    
-    template<>
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TimeSpan& target)
-          {
-            return bind (Link::mutateTimeSpan, ref(target), _1 );
-          }
-      };
-    
-    
-    
-    
-    
-    
-    template
-    template
-    void
-    Mutator::bind_to (TAR& target)  const
-    {
-      setVal_ = Policy::buildChangeHandler (target);
-      offset_ = Policy::buildChangeHandler (target);
-      nudge_  = Policy::buildChangeHandler (target);
-    }
-    
-    template
-    void
-    Mutator::unbind()
-    {
-      setVal_ = ValueSetter();
-      offset_ = Ofsetter();
-      nudge_  = Nudger();
-    }
-    
-  }
   
   
   /**
    * Frontend/Interface: controller-element to retrieve
-   * and change running time values
+   * and change running time values. time::Control is
+   * a mediator element, which can be attached to some
+   * time value entities as \em mutation, and at the
+   * same time allows to register listeners. When
+   * configured this way, \em changes may be fed
+   * to the function operator(s). These changes
+   * will be imposed to the connected target
+   * and the result propagated to the listeners.
    * 
    * @see time::Mutation
    * @see time::TimeSpan#accept(Mutation const&)
-   * @todo WIP-WIP-WIP
    */
   template
   class Control
@@ -460,9 +136,9 @@ namespace time {
       virtual void change (QuTime&)    const;
         
     public:
-      void operator() (TI const&);
-      void operator() (Offset const&);
-      void operator() (int);
+      void operator() (TI const&)      const;
+      void operator() (Offset const&)  const;
+      void operator() (int)            const;
       
       
       /** install a callback functor to be invoked as notification
@@ -481,27 +157,47 @@ namespace time {
   
   /* === forward to implementation === */
   
+  /** impose a new value to the connected target.
+   *  If applicable, the target will afterwards reflect
+   *  that change, and listeners will be notified, passing
+   *  the target's new state.
+   * @throw error::State if not connected to a target
+   * @note the actual change in the target also depends
+   *       on the concrete target type and the type of
+   *       the change. By default, the time value is
+   *       changed; this may include grid alignment.
+   */
   template
   void
-  Control::operator () (TI const& newValue)
+  Control::operator () (TI const& newValue)  const
   {
     this->ensure_isArmed();
     notifyListeners_(
         this->setVal_(newValue));
   }
   
+  /** impose an offset to the connected target.
+   *  If applicable, the target will be adjusted by the
+   *  time offset, and listeners will be notified.
+   * @throw error::State if not connected to a target
+   */
   template
   void
-  Control::operator () (Offset const& adjustment)
+  Control::operator () (Offset const& adjustment)  const
   {
     this->ensure_isArmed();
     notifyListeners_(
         this->offset_(adjustment));
   }
   
+  /** nudge the connected target by the given offset steps,
+   *  using either the target's own grid (when quantised),
+   *  or a 'natural' nudge grid
+   * @throw error::State if not connected to a target
+   */
   template
   void
-  Control::operator () (int offset_by_steps)
+  Control::operator () (int offset_by_steps)  const
   {
     this->ensure_isArmed();
     notifyListeners_(
@@ -530,6 +226,9 @@ namespace time {
     notifyListeners_.attach (toNotify);
   }
   
+  
+  /* ==== Implementation of the Mutation interface ==== */
+  
   template
   void
   Control::change (Duration& targetDuration)  const
diff --git a/tests/40components.tests b/tests/40components.tests
index 4541270c7..999817faa 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -688,7 +688,22 @@ return: 0
 END
 
 
-PLANNED "Life changing time specifications with feedback" TimeControl_test  <
@@ -57,7 +58,10 @@ namespace test{
   using lumiera::typelist::InstantiateChainedCombinations;
   using error::LUMIERA_ERROR_UNCONNECTED;
   
-  namespace {
+  
+  
+  namespace { // Test setup and helpers....
+    
     inline string
     pop (Arg arg)
     {
@@ -105,21 +109,25 @@ namespace test{
             return *received_;
           }
       };
-  }
+      
+  }//(End)Test helpers
   
   
   
   
-  /****************************************************************
-   * @test use the time::Control to push a sequence of modifications
-   *       to various time entities; in all cases, a suitable change
-   *       should be imposed to the target and then a notification signal
+  /***********************************************************************
+   * @test use the time::Control to push a sequence of modifications to
+   *       various time entities; in all cases, a suitable change should
+   *       be imposed to the target and then a notification signal
    *       should be invoked.
-   * @todo cover the cases.....
-   *       - change to a given value
-   *       - change by an offset
-   *       - change using a grid value
-   *       - apply an (grid) increment
+   *       
+   *       After covering a simple basic case, this test uses
+   *       template metaprogramming techniques to build a matrix of all
+   *       possible type combinations and then performs a standard test
+   *       sequence for each of these type combinations. Within this
+   *       test sequence, verification functions are invoked, which
+   *       are defined with specialisations to adapt for the various
+   *       types to be covered.
    */
   class TimeControl_test : public Test
     {
@@ -176,6 +184,11 @@ namespace test{
         }
       
       
+      /** @test cover all possible combinations of input change values
+       *        and target time value entities to be handled by time::Control.
+       *        Each of these cases executes a standard test sequence, which is
+       *        defined in TestCase#performTestSequence
+       */
       void verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c);
     };
   
@@ -241,7 +254,7 @@ namespace test{
             return QuTime (org, "test_grid_PAL");
           }
       };
-   
+    
     
     template
     struct TestChange
@@ -395,17 +408,21 @@ namespace test{
     
     
     
-    template
+    template< class TAR     ///< type of the target time value entity to receive changes
+            , class SRC     ///< type of the time value to be imposed as change
+            , class BASE
+            >
     struct TestCase
       : BASE
       {
         void
-        performTestCases(TimeValue const& org, TimeValue const& c)
+        performTestSequence(TimeValue const& org, TimeValue const& c)
           {
             cout << "Test-Case. Target=" << showType() 
-                 <<" <--feed--- "        << showType() 
-                 <() 
+                 << endl;
             
+            // test subject
             Control controller;
             
             TAR target = TestTarget::build(org);
@@ -453,32 +470,35 @@ namespace test{
             
             
             // tail recursion: further test combinations....
-            BASE::performTestCases(org,c);
+            BASE::performTestSequence(org,c);
           }
       };
     
     struct IterationEnd
       {
-        void performTestCases(TimeValue const&, TimeValue const&) { }
+        void performTestSequence(TimeValue const&, TimeValue const&) { }
       };
     
   }//(End)Implementation Test-case matrix
   
   
   void
-  TimeControl_test::verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c)
+  TimeControl_test::verifyMatrix_of_MutationCases (TimeValue const& origVal, TimeValue const& change)
   {
-    typedef Types KindsOfTarget;
-    typedef Types KindsOfSource;
+    typedef Types                KindsOfTarget;  // time entities to receive value changes
+    typedef Types KindsOfSource;  // time entities to be used as change values
     typedef InstantiateChainedCombinations< KindsOfTarget
                                           , KindsOfSource
-                                          , TestCase
+                                          , TestCase                       // template to be instantiated for each type
                                           , IterationEnd > TestMatrix;
     
-    TestMatrix().performTestCases(o,c);
+    TestMatrix().performTestSequence(origVal, change);
   }
   
   
+  
+  
+  
   /** Register this test class... */
   LAUNCHER (TimeControl_test, "unit common");
   

From 971dea6f6aa5c85333fa70e2e7e4f42b840932c2 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 13 Jun 2011 20:07:30 +0200
Subject: [PATCH 037/296] PlayService: basic definition and link to facade

---
 src/common/interface-facade-link.hpp   | 123 +++++++++++++++++++++++++
 src/common/interfaceproxy.cpp          |   2 +-
 src/include/interfaceproxy.hpp         |  40 ++++++--
 src/include/play-facade.h              |  11 ++-
 src/lib/error.hpp                      |   1 +
 src/lib/exception.cpp                  |   1 +
 src/lib/handle.hpp                     |   2 +-
 src/proc/facade.hpp                    |   7 +-
 src/proc/play/dummy-player-service.hpp |   2 +-
 src/proc/play/play-service.cpp         |  40 +++++++-
 src/proc/play/play-service.hpp         |  11 ++-
 wiki/renderengine.html                 |  27 ++++--
 12 files changed, 238 insertions(+), 29 deletions(-)
 create mode 100644 src/common/interface-facade-link.hpp

diff --git a/src/common/interface-facade-link.hpp b/src/common/interface-facade-link.hpp
new file mode 100644
index 000000000..27c0826c9
--- /dev/null
+++ b/src/common/interface-facade-link.hpp
@@ -0,0 +1,123 @@
+/*
+  INTERFACE-FACADE-LINK  -  a switchable link from interface to service implementation
+
+  Copyright (C)         Lumiera.org
+    2011,               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 interface-facade-link.hpp
+ ** Opening, accessing and closing the service access through a facade interface.
+ ** Client code is assumed to access an application level service through an facade
+ ** interface, while the actual implementation object remains an opaque internal detail.
+ ** Moreover, services may come up and shut down, so the implementation might change
+ ** during the Lifecycle. The facility defined here in this header provides a basic
+ ** implementation for this access mechanism, but without any adaptation, binding
+ ** or plugin access layer. It works only under the assumption that both the 
+ ** interface and the actual service implementation coexist in the same
+ ** executable and are written in C++, so any invocation of an 
+ ** interface method boils down to a language-level call.
+ ** 
+ ** Usually, client code doesn't need to include this header. Clients are assumed
+ ** to use the facade interface of the service in question. This facade interface
+ ** contains a static member of type \c lumiera::facade::Accessor (where I is
+ ** the type of the facade interface). The Accessor baseclass is defined in
+ ** interfaceproxy.hpp and typically included through the facade header.
+ ** 
+ ** @note there is a way more elaborate implementation of the same mechanism
+ **       for use with the Lumiera Interface/Plugin system.
+ **
+ ** @see interfaceproxy.hpp description of the more general use case
+ ** @see PlayService example for the simple use case
+ */
+
+
+#ifndef LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
+#define LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
+
+
+#include "lib/error.hpp"
+#include "lib/test/test-helper.hpp"
+#include "include/interfaceproxy.hpp"
+#include "lib/symbol.hpp"
+
+#include 
+
+
+
+namespace lumiera {
+namespace facade {
+  
+  using lib::Literal;
+  
+  
+  /************************************************************************
+   * simple access-frontend to the implementation of a service (C++ only).
+   * Usually, an instance of Accessor is placed as static member right into
+   * the facade interface used to access the service. This implementation
+   * of the access mechanism handles the simple case that both the facade
+   * and the service implementation are written in C++ and calls happen
+   * within the main executable as direct language calls, without an
+   * binding layer and without involving the Interface/Plugin system.
+   * 
+   * Typically, the InterfaceFacadeLink becomes a member of the service
+   * implementation class and is directly tied into the constructor of
+   * the latter. Being a subclass of lumiera::facade::Accessor, it is
+   * allowed to "open" the facade access just by setting the static
+   * protected pointer Accessor::implProxy_
+   */
+  template
+  class InterfaceFacadeLink
+    : protected Accessor
+    , boost::noncopyable
+    {
+      Literal displayName_;
+      
+      void
+      __checkLifecycle ()
+        {
+          if (Accessor::implProxy_)
+            throw error::State("Attempt to open an already opened Facade interface."
+                              , error::LUMIERA_ERROR_LIFECYCLE);
+        }
+      
+    public:
+      InterfaceFacadeLink(FA& serviceImpl, Literal interfaceName_for_Log=0)
+        : displayName_(lib::test::showType(interfaceName_for_Log))
+        {
+          __checkLifecycle();
+          Accessor::implProxy_ = &serviceImpl;
+          INFO (interface, "interface %s opened", displayName_.c());
+        }
+      
+     ~InterfaceFacadeLink()
+        {
+          INFO (interface, "closing interface %s...", displayName_.c());
+          Accessor::implProxy_ = 0;
+        }
+    };
+  
+  
+  
+  /** storage for the static access pointer */
+  template
+  FA* Accessor::implProxy_;
+  
+  
+}} // namespace lumiera::facade
+
+#endif
diff --git a/src/common/interfaceproxy.cpp b/src/common/interfaceproxy.cpp
index 611bdd97c..814f991b4 100644
--- a/src/common/interfaceproxy.cpp
+++ b/src/common/interfaceproxy.cpp
@@ -32,7 +32,7 @@ namespace lumiera{
 namespace facade {
   
   
-  LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible");
+  LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade is closed; service currently not accessible");
   
   
   /**
diff --git a/src/include/interfaceproxy.hpp b/src/include/interfaceproxy.hpp
index 54084f8fd..0469da8f9 100644
--- a/src/include/interfaceproxy.hpp
+++ b/src/include/interfaceproxy.hpp
@@ -48,17 +48,18 @@
  ** Any sort of dependency management is outside the scope of the InstanceHandle (for the core
  ** services, it is handled by the dependency of subsystems, while the plugin loader cares
  ** for dependency issues regarding loadable modules, thereby building on the deployment
- ** descriptors.
+ ** descriptors.)
  ** 
  ** For the Layer separation interfaces, the process of loading and opening is abstracted as
  ** an InstanceHandle object. When creating such an InstanceHandle using the appropriate
  ** template and ctor parameters, in addition to the registration with the Interface/Plugin 
- ** system, the corresponding facade::Proxy factory is addressed and "opened" by creating
- ** the right proxy object instance. Similarly, when the InstanceHandle object goes out
- ** of scope, prior to detaching from the Interface/Proxy system, the corresponding 
- ** lumiera::facade::Accessor factory is "closed", which additionally means destroying
- ** the proxy object instance and switching any further access to throwing and exception. 
- **
+ ** system, the corresponding facade::Proxy factory is addressed and the interface instance
+ ** is "opened" by creating the appropriate proxy object instance. Similarly, when the
+ ** InstanceHandle object goes out of scope, prior to detaching from the Interface/Proxy
+ ** system, the corresponding lumiera::facade::Accessor frontend is "closed", which
+ ** additionally means destroying the proxy object instance and switching any
+ ** further access to throwing and exception. 
+ ** 
  ** While client code just includes the interface header (including interfaceproxy.hpp
  ** in turn), there needs to be an actual implementation of each proxy object located in
  ** some translation unit. The usual place is interfaceproxy.cpp, which gets linked into 
@@ -89,7 +90,29 @@ namespace facade {
 
 
   /*********************************************************************
+   * access-frontend to the implementation of a service.
+   * Usually, an instance of Accessor is placed as static member
+   * right into the facade interface used to access the service.
+   * This allows clients to get the current actual implementation
+   * of that service, just by invoking the function operator on
+   * that member, e.g. \c lumiera::Play::facade()
    * 
+   * The reason for this rather indirect access technique is Lifecycle:
+   * Service implementations may come up and go down; moreover, a service
+   * might be implemented through a plugin component and thus the actual
+   * invocation needs to be passed through a binding layer. In this case,
+   * clients rather access a proxy object, which then passes on any call
+   * through that binding layer to the actual implementation located
+   * "somewhere".
+   * 
+   * @note the pointer to the actual implementation is a static variable.
+   *       This has two consequences. For one, we're dealing with kind of
+   *       singleton service here. And, secondly, the implementation or
+   *       proxy accessor can inherit from Accessor where FA is the
+   *       facade interface. Being a subclass, allows the implementation
+   *       to set that pointer when the service comes up, and to clear
+   *       it when the service goes down and the access needs to
+   *       be closed.
    */
   template
   class Accessor
@@ -105,7 +128,8 @@ namespace facade {
           if (implProxy_)
             return *implProxy_;
           else
-            throw error::State("Facade interface currently closed.");
+            throw error::State("Facade interface currently closed."
+                              , LUMIERA_ERROR_FACADE_LIFECYCLE);
         }
     };
   
diff --git a/src/include/play-facade.h b/src/include/play-facade.h
index bc8fa26e9..84865444c 100644
--- a/src/include/play-facade.h
+++ b/src/include/play-facade.h
@@ -115,15 +115,16 @@ namespace lumiera {
           };
         
         
-        typedef lib::IterSource ModelPorts;
-        typedef lib::IterSource Pipes;
+        typedef lib::IterSource&    ModelPorts;
+        typedef lib::IterSource& Pipes;
         typedef proc::play::POutputManager Output;
         typedef mobject::session::PClipMO Clip;
-        typedef mobject::PTrack Track;
+        typedef mobject::PTrack  Track;
         typedef asset::PTimeline Timeline;
         typedef asset::PViewer Viewer;
-          
-        /** create a new playback process outputting to the given viewer/display */
+        
+        /** core operation: create a new playback process
+         *  outputting to the given viewer/display  */
         virtual Controller connect(ModelPorts, Output)      =0;
         
         
diff --git a/src/lib/error.hpp b/src/lib/error.hpp
index e96942ee6..5e2427e0a 100644
--- a/src/lib/error.hpp
+++ b/src/lib/error.hpp
@@ -122,6 +122,7 @@ namespace lumiera {
     LUMIERA_ERROR_DECLARE (ASSERTION);    ///< assertion failure
     
     /* generic error situations */
+    LUMIERA_ERROR_DECLARE (LIFECYCLE);    ///< Lifecycle assumptions violated
     LUMIERA_ERROR_DECLARE (WRONG_TYPE);   ///< runtime type mismatch
     LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached
     LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value
diff --git a/src/lib/exception.cpp b/src/lib/exception.cpp
index 82e228687..8c524e5d4 100644
--- a/src/lib/exception.cpp
+++ b/src/lib/exception.cpp
@@ -79,6 +79,7 @@ namespace lumiera {
     LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure");
     
     /* some further generic error situations */
+    LUMIERA_ERROR_DEFINE (LIFECYCLE, "Lifecycle assumptions violated");
     LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch");
     LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached");
     LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value");
diff --git a/src/lib/handle.hpp b/src/lib/handle.hpp
index a1a01a919..685a6a4c6 100644
--- a/src/lib/handle.hpp
+++ b/src/lib/handle.hpp
@@ -60,7 +60,7 @@ namespace lib {
    * Generic opaque reference counting handle, for accessing a service
    * and managing its lifecycle. Usually such a handle is created by
    * an service interface and \link #activate activated \endlink by
-   * setting up the link to some internal implementation object.
+   * setting up the link to a suitable hidden implementation object.
    * This setup can only be done by a friend or derived class,       //////////////////////////TODO: that was the intention. Why didn't this work out as expected?
    * while client code is free to copy and store handle objects.
    * Finally, any handle can be closed, thereby decrementing
diff --git a/src/proc/facade.hpp b/src/proc/facade.hpp
index e32227729..18ef46454 100644
--- a/src/proc/facade.hpp
+++ b/src/proc/facade.hpp
@@ -57,14 +57,15 @@ namespace proc {
       
       
       /** provide a descriptor for lumiera::AppState,
-       *  wired accordingly to allow main to load and
-       *  save an existing session. */
+       *  wired accordingly to allow main to bring up
+       *  a editing session, possibly by loading an
+       *  existing session from storage. */
       static lumiera::Subsys& getSessionDescriptor();
       
       
       /** provide a descriptor for lumiera::AppState,
        *  wired accordingly to allow main to bring up
-       *  the render, playback coordination and 
+       *  the render / playback coordination and 
        *  output management subsystem. */
       static lumiera::Subsys& getPlayOutDescriptor();
       
diff --git a/src/proc/play/dummy-player-service.hpp b/src/proc/play/dummy-player-service.hpp
index 5cf8438ac..d5f942b3e 100644
--- a/src/proc/play/dummy-player-service.hpp
+++ b/src/proc/play/dummy-player-service.hpp
@@ -72,7 +72,7 @@ namespace proc {
     /********************************************************************
      * Actual implementation of a single (dummy) playback process.
      * The DummyPlayerService (see below) maintains a collection of such
-     * actively running playback processes, while the client code gets 
+     * actively running playback processes, while the client code gets
      * DummyPlayer::Process handles to track any ongoing use. Users of
      * the plain C interface get a direct bare pointer to the respective
      * ProcessImpl instance and have to manage the lifecycle manually.
diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp
index 7ee13c480..52d0ac789 100644
--- a/src/proc/play/play-service.cpp
+++ b/src/proc/play/play-service.cpp
@@ -32,6 +32,16 @@
 
 
 
+namespace lumiera {
+  
+  
+  Play::~Play() { } // emit VTables here...
+  
+  
+  
+}//(End) namespace lumiera
+
+
 namespace proc {
 namespace play {
 
@@ -55,9 +65,37 @@ namespace play {
   } // (End) hidden service impl details
   
   
+  using lumiera::Play;
   
   
-  /** */
+  /** bring up the global render- and playback service.
+   *  This service allows to create individual PlayProcess instances
+   *  to \em perform a timeline or similar model object, creating
+   *  rendered data for output. Client code is assumed to access
+   *  this service through the lumiera::Play facade. 
+   */
+  PlayService::PlayService()   /////TODO Subsys::SigTerm terminationHandle);
+    : facadeAccess_(*this, "Player")
+    { }
+  
+  
+  
+  /**
+   * @note this is the core operation of the play and render service
+   * 
+   * Invoking this function investigates the given exit nodes of the
+   * render nodes network and retrieves actual output destinations
+   * through the given OutputManager. The goal is to configure an 
+   * PlayProcess, based on the renderengine and the collection of
+   * OutputSlot instances retrieved for each of the given exit nodes.
+   * Running this PlayProcess will activate the render engine to deliver
+   * calculated media data to the outputs. 
+   */
+  Play::Controller
+  PlayService::connect(ModelPorts dataGenerators, Output outputDestinations)
+  {
+    UNIMPLEMENTED ("build a PlayProcess");
+  }
 
 
 }} // namespace proc::play
diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp
index 03780f134..29db10187 100644
--- a/src/proc/play/play-service.hpp
+++ b/src/proc/play/play-service.hpp
@@ -34,6 +34,7 @@
 
 #include "lib/error.hpp"
 #include "include/play-facade.h"
+#include "common/interface-facade-link.hpp"
 
 #include 
 //#include 
@@ -44,6 +45,7 @@ namespace proc {
 namespace play {
 
   using std::string;
+  using lumiera::facade::InterfaceFacadeLink;
 //using lumiera::Subsys;
 //using lumiera::Display;
 //using lumiera::DummyPlayer;
@@ -58,8 +60,15 @@ namespace play {
    * Interface: Player subsystem.
    */
   class PlayService
-    : boost::noncopyable
+    : public lumiera::Play
+    , boost::noncopyable
     {
+      InterfaceFacadeLink facadeAccess_;
+      
+      
+      /** Implementation: build a PlayProcess */
+      virtual Controller connect(ModelPorts, Output);
+      
       
     public:
       PlayService();   /////TODO Subsys::SigTerm terminationHandle);
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 1c1558952..d5ee8e508 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1632,7 +1632,7 @@ The main tool used to implement this separation is the [[Builder Pattern|http://
 Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
 
-
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
 
 !Starting point
@@ -1658,7 +1658,7 @@ Reworking the dummy player into the [[Player subsystem|PlayService]] is a larger
 * then, of course, the PlayService interface needs to be created
 * as a temporary soltion, a ''~DummyPlayConnection'' will be created hard-wired, within the ~DummyPlayer service. This dumy container is a test setup, explicitly creating a OutputManager implementation, a GeneratorMO, a GeneratorNode (thus omitting the not-yet-implemented Builder) and the corresponding ModelPort. This way, the ~DummyPlayer service can be changed to use the real PlayService for creating the dummy generated output data. It will then pass back the resulting PlayController to the existing GUI setup.
 
-The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach tries to circumvent the problems with occasional crashes on shutdown, due to dispatching frame output signals in the event thread while already in shutdown.
+The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach we hope to circumvent problems with occasional crashes on closing the application, due to dispatching frame output signals in the event thread while shutdown is already in progress.
 
 
@@ -3219,14 +3219,15 @@ While actually data frames are //pulled,// on a conceptual level data is assumed As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
 * thus, any designation is a [[Pipe]]-ID.
 * consequently, it is not obviously clear if such an designation is the final exit point
-* please note the [[Engine interface proposal|http://lumiera.org/Lumiera/DesignProcess/EngineInterfaceOverview.html]]
+* please note the [[Engine interface proposal|http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceOverview.html]]
 * this introduces the notion of a ModelPort: //a point in the (high level) model where output can be produced//
+* thus obviously we need an OutputManager element to track the association of OutputDesignation to OutputSlot
 
 Do we get a single [[Fixture]] &mdash; guess yes
 
@@ -3242,10 +3243,20 @@ We should note that in both cases this mapping operation is controlled and drive
 !Connection to external outputs
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
-Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle timeouts gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
+Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
 &rarr; see also the PlayerDummy
+
+!the global output manager
+While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager''. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities.
 
+
+
The term &raquo;''Output Manager''&laquo; might deonte two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global OutputManager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
+
+&rarr; see [[output management overview|OutputManagement]]
+&rarr; see OutputSlot
+&rarr; see ViewerPlayConnection
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
@@ -7285,14 +7296,14 @@ These Viewer (or Monitor) elements play an enabling role for any output generati
 
 When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.
-
+
for showing output, three entities are involved
 * the [[Timeline]] holds the relevant part of the model, which gets rendered for playback
-* by connecting to a viewer component, an actual output target is established
+* by connecting to a viewer component (&rarr; ViewConnection), an actual output target is established
 * the playback process itself is coordinated by a PlayController, which in turn creates a PlayProcess
 
 !the viewer connection
-A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputManager slot &mdash; again an output mapping step.
+A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputSlot registered with the OutputManager &mdash; again an output mapping step.
 
From 1b0cb56dccd88789084c0e1995edd303ecf46d02 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 14 Jun 2011 05:41:02 +0200 Subject: [PATCH 038/296] implement PlayOut subsystem and draft OutputDirector --- src/backend/thread-wrapper.hpp | 2 +- src/common/guifacade.cpp | 2 +- src/common/subsys.hpp | 11 +- src/common/subsystem-runner.hpp | 2 +- src/lib/result.hpp | 9 +- src/proc/control/handling-pattern.hpp | 1 + src/proc/facade.cpp | 17 ++- .../mobject/session/lifecycle-advisor.hpp | 4 +- .../mobject/session/sess-manager-impl.cpp | 14 +- .../mobject/session/sess-manager-impl.hpp | 2 +- src/proc/play/output-director.cpp | 135 ++++++++++++++++++ src/proc/play/output-director.hpp | 100 +++++++++++++ src/proc/play/output-manager.cpp | 44 ------ 13 files changed, 270 insertions(+), 73 deletions(-) create mode 100644 src/proc/play/output-director.cpp create mode 100644 src/proc/play/output-director.hpp delete mode 100644 src/proc/play/output-manager.cpp diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index 60413b037..699bfd11d 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -200,7 +200,7 @@ namespace backend { /** Synchronisation barrier. In the function executing in this thread - * needs to be a corresponding Thread::sync() call. Blocking until + * needs to be a corresponding Thread::syncPoint() call. Blocking until * both the caller and the thread have reached the barrier. */ void diff --git a/src/common/guifacade.cpp b/src/common/guifacade.cpp index aff7febb8..5e1c4599d 100644 --- a/src/common/guifacade.cpp +++ b/src/common/guifacade.cpp @@ -156,7 +156,7 @@ namespace gui { if (facade) { WARN (guifacade, "GUI subsystem terminates, but GuiFacade isn't properly closed. " - "Closing it forcedly; this indicates broken startup logic and should be fixed."); + "Closing it forcedly; this indicates broken startup logic and should be fixed."); try { facade.reset (0); } catch(...) { WARN_IF (lumiera_error_peek(), guifacade, "Ignoring error: %s", lumiera_error()); } lumiera_error(); // clear any remaining error state... diff --git a/src/common/subsys.hpp b/src/common/subsys.hpp index 363b17e51..26b080861 100644 --- a/src/common/subsys.hpp +++ b/src/common/subsys.hpp @@ -104,9 +104,12 @@ namespace lumiera { /** initiate termination of this subsystem. - * may be called repeatedly any time... - * @warning must not block nor throw. */ - virtual void triggerShutdown () throw() =0; + * This trigger may be called repeatedly any time... + * When the subsystem actually has terminated, + * the SigTerm passed to #start must be invoked. + * @note called within a locked context (barrier) + * @warning must not block nor throw. */ + virtual void triggerShutdown () throw() =0; const std::vector @@ -114,7 +117,7 @@ namespace lumiera { private: - /** weather this subsystem is actually operational. + /** whether this subsystem is actually operational. * When returning \c false here, the application may * terminate at any point without further notice * Note further, that a subsystem must not be in diff --git a/src/common/subsystem-runner.hpp b/src/common/subsystem-runner.hpp index 89a90245d..6cbd3495a 100644 --- a/src/common/subsystem-runner.hpp +++ b/src/common/subsystem-runner.hpp @@ -173,7 +173,7 @@ namespace lumiera { if (!and_all (susy->getPrerequisites(), isRunning() )) { susy->triggerShutdown(); - throw error::Logic("Unable to start all prerequisites of Subsystem "+string(*susy)); + throw error::State("Unable to start all prerequisites of Subsystem "+string(*susy)); } } void diff --git a/src/lib/result.hpp b/src/lib/result.hpp index ae4f7b187..daf153a49 100644 --- a/src/lib/result.hpp +++ b/src/lib/result.hpp @@ -58,11 +58,10 @@ namespace lib { /** - * Result value and status of some operation. - * It can be created for passing a result produced - * by the operation, or the failure to do so. The value - * can be retrieved by implicit or explicit conversion. - * @throws on any attempt to access the value in case of failure + * Optional Result value or status of some operation. + * It can be created for passing a result produced by the operation, or the + * failure to do so. The value can be retrieved by implicit or explicit conversion. + * @throw error::State on any attempt to access the value in case of failure * @warning this class has a lot of implicit conversions; * care should be taken when defining functions * to take Result instances as parameter.... diff --git a/src/proc/control/handling-pattern.hpp b/src/proc/control/handling-pattern.hpp index f414e25a1..6cc947211 100644 --- a/src/proc/control/handling-pattern.hpp +++ b/src/proc/control/handling-pattern.hpp @@ -67,6 +67,7 @@ namespace control { * It is returned when invoking a HandlingPattern * and can be used to check for success and/or re-throw * any Exception encountered during the command execution. + * @todo couldn't that be replaced by a lib::Result instance?? */ class ExecResult : public lib::BoolCheckable diff --git a/src/proc/facade.cpp b/src/proc/facade.cpp index 6ca6a84b6..de6d562ee 100644 --- a/src/proc/facade.cpp +++ b/src/proc/facade.cpp @@ -23,6 +23,7 @@ #include "proc/facade.hpp" #include "lib/singleton.hpp" +#include "proc/play/output-director.hpp" #include @@ -77,7 +78,7 @@ namespace proc { bool shouldStart (lumiera::Option&) { - TODO ("determine, if an existing Session schould be loaded"); + TODO ("determine, if an existing Session should be loaded"); return false; } @@ -127,22 +128,24 @@ namespace proc { bool start (lumiera::Option&, Subsys::SigTerm termination) { - UNIMPLEMENTED ("bring up and configure the output connections and the player"); - return false; + this->completedSignal_ = termination; + return play::OutputDirector::instance().connectUp(); } + SigTerm completedSignal_; + + void triggerShutdown () throw() { - UNIMPLEMENTED ("initiate playback/render halt and disconnect output"); + play::OutputDirector::instance().triggerDisconnect (completedSignal_); } + bool checkRunningState () throw() { - //Lock guard (*this); - TODO ("implement detecting running state"); - return false; + return play::OutputDirector::instance().isOperational(); } }; diff --git a/src/proc/mobject/session/lifecycle-advisor.hpp b/src/proc/mobject/session/lifecycle-advisor.hpp index a3c286af8..0e8364fae 100644 --- a/src/proc/mobject/session/lifecycle-advisor.hpp +++ b/src/proc/mobject/session/lifecycle-advisor.hpp @@ -31,7 +31,7 @@ ** here; any implementation is delegated to the relevant session facilities. ** ** The idea of a LifecycleAdvisor is inspired by GUI frameworks, especially - ** Spring RichClient. Typically, such frameworks provides a means for flexible + ** Spring RichClient. Typically, such frameworks provide a means for flexible ** configuration of the application lifecycle. Configurability isn't the primary ** goal here, as there is only one Lumiera application and the session lifecycle ** can be considered fixed, with the exception of some extension points, which are @@ -64,7 +64,7 @@ namespace session { /** - * Skeleton operations conducting the session lifecycle sequences. + * Skeleton of operations conducting the session lifecycle sequences. * Any details of the operations are delegated to the current session * and associated services. * @warning this object is assumed to be used as a single instance diff --git a/src/proc/mobject/session/sess-manager-impl.cpp b/src/proc/mobject/session/sess-manager-impl.cpp index 972a711be..9ce3c61ec 100644 --- a/src/proc/mobject/session/sess-manager-impl.cpp +++ b/src/proc/mobject/session/sess-manager-impl.cpp @@ -67,7 +67,7 @@ namespace session { SessionImplAPI* SessManagerImpl::operator-> () throw() { - if (!pImpl_) + if (!pSess_) try { // create empty default configured session this->reset(); @@ -80,7 +80,7 @@ namespace session { } - return pImpl_.get(); + return pSess_.get(); } @@ -201,8 +201,8 @@ namespace session { * \link #operator-> access \endlink to the session object. */ SessManagerImpl::SessManagerImpl () throw() - : pImpl_ (0) - , lifecycle_(new SessionLifecycleDetails(pImpl_)) + : pSess_ (0) + , lifecycle_(new SessionLifecycleDetails(pSess_)) { Session::initFlag = true; //////////////////////////////////////// TICKET #518 instead of this hack, implement basic-init of the session manager for real } @@ -219,7 +219,7 @@ namespace session { bool SessManagerImpl::isUp () { - return bool(pImpl_); ///////////////////// TICKET #702 possible race, because this gets true way before the interface is up + return bool(pSess_); ///////////////////// TICKET #702 possible race, because this gets true way before the interface is up } /** @note no transactional behaviour. may succeed partially. @@ -229,7 +229,7 @@ namespace session { SessManagerImpl::clear () { Lock sync(this); - pImpl_->clear(); + pSess_->clear(); } @@ -242,7 +242,7 @@ namespace session { { Lock sync(this); lifecycle_->shutDown(); - pImpl_.reset(); + pSess_.reset(); } diff --git a/src/proc/mobject/session/sess-manager-impl.hpp b/src/proc/mobject/session/sess-manager-impl.hpp index 14af631c6..403f835c8 100644 --- a/src/proc/mobject/session/sess-manager-impl.hpp +++ b/src/proc/mobject/session/sess-manager-impl.hpp @@ -42,7 +42,7 @@ namespace session { : public SessManager , public lib::Sync<> { - scoped_ptr pImpl_; + scoped_ptr pSess_; scoped_ptr lifecycle_; SessManagerImpl() throw(); diff --git a/src/proc/play/output-director.cpp b/src/proc/play/output-director.cpp new file mode 100644 index 000000000..0bcbebc26 --- /dev/null +++ b/src/proc/play/output-director.cpp @@ -0,0 +1,135 @@ +/* + OutputDirector - handling all the real external output connections + + Copyright (C) Lumiera.org + 2011, 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/play/play-service.hpp" +#include "proc/play/output-manager.hpp" +#include "proc/play/output-director.hpp" +#include "backend/thread-wrapper.hpp" + +using backend::Thread; + + +namespace proc { +namespace play { + + + + namespace { // hidden local details of the service implementation.... + + } // (End) hidden service impl details + + + + /** storage for the single application wide OutputDirector instance */ + lib::Singleton OutputDirector::instance; + + + /** bring up the framework for handling input/output connections. + * Creating this object happens on first access and shouldn't be + * confused with actually booting up / shutting down this subsystem. + * Rather, the purpose of the OutputDirector is actively to conduct + * the Lifecycle of booting, connecting, operating, disconnecting. + */ + OutputDirector::OutputDirector() + { } + + OutputDirector::~OutputDirector() { } + + + + /** connect and bring up the external input/output connections, + * handlers and interface services and the render/playback service. + * @return true if the output subsystem can be considered operational + * + * @todo WIP-WIP-WIP 6/2011 + */ + bool + OutputDirector::connectUp() + { + Lock sync(this); + + player_.reset (new PlayService); + return this->isOperational(); + } + + + bool + OutputDirector::isOperational() const + { + return bool(player_); ////////////TODO more to check here.... + } + + + + /** initiate shutdown of all ongoing render/playback processes + * and closing of all external input/output interfaces. + * Works as an asynchronous operation; the given callback signal + * will be invoked when the shutdown is complete. + * @note starting a new thread, which might fail. + * When this happens, the raised exception will cause + * immediate unconditional termination of the application. + */ + void + OutputDirector::triggerDisconnect (SigTerm completedSignal) throw() + { + Thread ("Output shutdown supervisor", bind (&OutputDirector::bringDown, this, completedSignal)); + } + + + /** @internal actually bring down any calculation processes + * and finally disconnect any external input/output interfaces. + * This shutdown and cleanup operation is executed in a separate + * "Output shutdown supervisor" thread and has the liability to + * bring down the relevant facilities within a certain timespan. + * When done, the last operation within this thread will be to + * invoke the callback signal given as parameter. + * @note locks the OutputDirector + */ + void + OutputDirector::bringDown (SigTerm completedSignal) + { + Lock sync(this); + string problemLog; + try + { + TODO ("actually bring down the output generation"); + player_.reset(0); + + completedSignal(0); + } + + catch (lumiera::Error& problem) + { + problemLog = problem.what(); + lumiera_error(); // reset error state + completedSignal (&problemLog); + } + catch (...) + { + problemLog = "Unknown error while disconnecting output. " + "Lumiera error flag is = "+string(lumiera_error()); + completedSignal (&problemLog); + } + } + +}} // namespace proc::play diff --git a/src/proc/play/output-director.hpp b/src/proc/play/output-director.hpp new file mode 100644 index 000000000..2783b097c --- /dev/null +++ b/src/proc/play/output-director.hpp @@ -0,0 +1,100 @@ +/* + OUTPUT-DIRECTOR.hpp - handling all the real external output connections + + Copyright (C) Lumiera.org + 2011, 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 output-director.hpp + ** A global service to coordinate and handle all external output activities. + ** The OutputDirector is an application internal singleton service for + ** coordinating and controlling all actual input/output- and rendering + ** capabilities, exposing distinct lifecycle functions to connect, bring + ** up and shut down what can be considered the "Player/Output" subsystem. + ** + ** @see output-manager-test.cpp ////TODO + */ + + +#ifndef PROC_PLAY_OUTPUT_DIRECTOR_H +#define PROC_PLAY_OUTPUT_DIRECTOR_H + + +#include "lib/error.hpp" +#include "lib/singleton.hpp" +#include "proc/play/output-manager.hpp" +#include "common/subsys.hpp" +#include "lib/sync.hpp" + +#include +//#include +//#include +//#include +#include + + +namespace proc { +namespace play { + +//using std::string; +//using std::vector; +//using std::tr1::shared_ptr; + using boost::scoped_ptr; + + + class PlayService; + + +//typedef lib::ScopedPtrVect DisplayerTab; + + + + /****************************************************** + * Management of external Output connections. + * + * @todo write Type comment + */ + class OutputDirector + : boost::noncopyable + , public lib::Sync<> + { + typedef lumiera::Subsys::SigTerm SigTerm; + + scoped_ptr player_; + + public: + static lib::Singleton instance; + + bool connectUp() ; + void triggerDisconnect(SigTerm) throw(); + + bool isOperational() const; + + private: + OutputDirector() ; + ~OutputDirector() ; + friend class lib::singleton::StaticCreate; + + + void bringDown (SigTerm completedSignal); + }; + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/output-manager.cpp b/src/proc/play/output-manager.cpp deleted file mode 100644 index 6ffca994f..000000000 --- a/src/proc/play/output-manager.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - OutputManager - handling all the real external output connections - - Copyright (C) Lumiera.org - 2011, 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/play/output-manager.hpp" - - - -namespace proc { -namespace play { - - - - namespace { // hidden local details of the service implementation.... - - } // (End) hidden service impl details - - - - - OutputSlot::~OutputSlot() { } - - - -}} // namespace proc::play From a37947641497b06248332e12d314af7f25a58d90 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 15 Jun 2011 03:51:06 +0200 Subject: [PATCH 039/296] create outline of PlayService, add stubs for Play::Controller --- src/proc/play/play-controller.cpp | 219 ++++++++++++++++++++++++++++++ src/proc/play/play-controller.hpp | 47 +++---- src/proc/play/play-process.hpp | 22 ++- src/proc/play/play-service.cpp | 79 ++++++++++- src/proc/play/play-service.hpp | 21 ++- wiki/renderengine.html | 4 +- 6 files changed, 356 insertions(+), 36 deletions(-) create mode 100644 src/proc/play/play-controller.cpp diff --git a/src/proc/play/play-controller.cpp b/src/proc/play/play-controller.cpp new file mode 100644 index 000000000..f8a3de185 --- /dev/null +++ b/src/proc/play/play-controller.cpp @@ -0,0 +1,219 @@ +/* + PlayProcess - frontend to control an play process + + Copyright (C) Lumiera.org + 2011, 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 play-controller.cpp + ** Implementation of the control- / management frontend(s) exposed + ** to clients for talking to an ongoing PlayProcess. This includes + ** the implementation of lumiera::Play::Controller, exposed directly + ** from the Player facade interface. Besides that primary control + ** interface, this translation unit also contains the implementation + ** for several extended play controllers targeted at special usage + ** situations + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#include "proc/play/play-controller.hpp" +#include "proc/play/play-process.hpp" +#include "lib/time/timevalue.hpp" + +//#include +//#include +//#include +//#include + +namespace lumiera { + + using lib::time::Time; + using lib::time::Duration; + using lib::time::TimeSpan; + + + + /* ==== Play::Controller implementation ==== */ + + /** toggle the play or pause state + * of the corresponding PlayProcess */ + void + Play::Controller::play(bool) + { + UNIMPLEMENTED ("toggle play/pause state"); + } + + + /** switch into scrubbing playback + * + */ + void + Play::Controller::scrub(bool) + { + UNIMPLEMENTED ("scrubbing playback"); + } + + + void + Play::Controller::adjustSpeed(double) ///< playback speed control + { + UNIMPLEMENTED ("adjust actual playback speed"); + } + + + /** + * + */ + void + Play::Controller::go(Time) ///< skip to the given point in time + { + UNIMPLEMENTED ("jump to given time"); + } + + + /** + * + */ + void + Play::Controller::controlPlayhead (time::Control
-
-
Within Lumiera, &raquo;Player&laquo; denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
+
+
Within Lumiera, &raquo;Player&laquo; is the name for a [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
 The player subsystem does not perform or even manage any render operations, nor does it handle the outputs directly.
 Yet it adresses some central concerns:
 

From ce6a917b598d59c3dadfb19b97bad7ef1690700c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 17 Jun 2011 04:07:07 +0200
Subject: [PATCH 040/296] first draft spec for the OutputSlot

---
 src/proc/mobject/output-designation.hpp |  6 ++-
 wiki/renderengine.html                  | 57 ++++++++++++++++++-------
 2 files changed, 46 insertions(+), 17 deletions(-)

diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp
index fd571e56a..e5d32813d 100644
--- a/src/proc/mobject/output-designation.hpp
+++ b/src/proc/mobject/output-designation.hpp
@@ -34,6 +34,8 @@ extern "C" {
 
 namespace mobject {
   
+  namespace mp = lumiera::typelist;
+  
   class MObject;
   
   template
@@ -99,8 +101,8 @@ namespace mobject {
       
     private:
       enum {
-        SPEC_SIZ = lumiera::typelist::maxSize<
-                       lumiera::typelist::Types::List>::value 
+        SPEC_SIZ = mp::maxSize<
+                       mp::Types< PID, lumiera_uid, uint>::List>::value 
       };
       typedef lib::OpaqueHolder SpecBuff;
       
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 3f9043064..f86c6087b 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -3159,7 +3159,7 @@ The operation point is provided by the current BuilderMould and used by the [[pr
 This is possible because the operation point has been provided (by the mould) with informations about the media stream type to be wired, which, together with information accessible at the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
 
-
+
An ever recurring problem in the design of Luimiera's ~Proc-Layer is how to refer to output destinations, and how to organise them.
 Wiring the flexible interconnections between the [[pipes|Pipe]] should take into account both the StreamType and the specific usage context ([[scope|PlacementScope]]) -- and the challenge is to avoid hard-linking of connections and tangling with the specifics of the target to be addressed and connected. This page, started __6/2010__ by collecting observations to work out the relations, arrives at defining a //key abstraction// of output management.
 
@@ -3183,21 +3183,21 @@ Wiring the flexible interconnections between the [[pipes|Pipe]] should take into
 * //indirect// means to derive the destination transitively (looking at the destination's output designation and so on)
 * //relative// in this context means that we refer to "the N^^th^^ of this kind" (e.g. the second video out)
 * we need to attach some metadata with an output; especially we need an associated StreamType
-* output designation is structured into several //levels://
+* the referral to an output designation can be observed on and is structured into several //levels://
 ** within the body of the model, mostly we address output destinations relatively
 ** at some point, we'll address a //subgroup// within the global pipes, which acts like a summation sink
-** there might be //master pipes//
-** finally, there is the hardware output or the distinct channel within the rendered result &mdash; never to be referred explicitly
+** there might be //master pipe(s),// collecting the output of various subgroups
+** finally, there is the hardware output or the distinct channel within the rendered result &mdash; never to be referenced explicitly
 !!!relation to Pipes
 in almost every case mentioned above, the output designation is identical with the starting point of a [[Pipe]]. This might be a global pipe or a ClipSourcePort. Thus it sounds reasonable to use pipe-~IDs directly as output designation. Pipes, as an accountable entity (=asset) just //leap into existence by being referred.// On the other hand, the //actual//&nbsp; pipe is a semantic concept, a specific structural arrangement of objects. Basically it means that some object acts as attachment point and thereby //claims//&nbsp; to be the entrance side of a pipe, while other processor objects chain up in sequence.
 !!!system outputs
-System level output connections seem to be an exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. But the question is if system outputs shall be directly addressable at all as output designation? Generally speaking, Lumiera is not intended to be a system wide connection manager or a real time performance software. Thus it's advisable to refrain from direct referrals to system level connections from within the model. Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen. So these external outputs become rather a matter of application configuration &mdash; and for all the other purposes we're free to ''use pipe-~IDs as output designation''.
+System level output connections seem to be an exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. Yet the question is if system outputs shall be directly addressable at all as output designation? Generally speaking, Lumiera is not intended to be a system wide connection manager or a real time performance software. Thus it's advisable to refrain from direct referrals to system level connections from within the model. Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen. So these external outputs become rather a matter of application configuration &mdash; and for all the other purposes we're free to ''use pipe-~IDs as output designation''.
 !!!consequences of mentioning an output designation
 The immediate consequence is that a [[Pipe]] with the given ID exists as an accountable entity. Only if &mdash; additionally &mdash; a suitable object within the model //claims to root this pipe,//  a connection to this designation is wired, using an appropriate [[processing pattern|ProcPatt]]. A further consequence then is for the mentioned output designation to become a possible candidate for a ModelPort, an exit node of the built render nodes network. By default, only those designations without further outgoing connections actually become active model ports (but an existing and wired pipe exit node can be promoted to a model port explicitly).
 &rarr; OutputManagement
 
 !!Challenge: mapping of output designations
-An entire sequence can be embedded within another sequence as a VirtualClip. While for a simple clip there is a Clip-~MObject placed into the model, holding an reference to a media asset, in case of a virtual clip an BindingMO takes on the role of the clip object. Note that BindingMO also acts as [[Timeline]] implementation &mdash; indeed the same sequence might be bound simultaneously as a timeline and into a virtual clip. This flexibility creates a special twist, as the contents of this sequence have no way of finding out if they're used as timeline or embedded virtual clip. So parts of this sequence might mention a OutputDesignation which, when using the sequence as virtual clip, needs to be translated into a virtual media channel, which can be treated in the same fashion as any channel (video, audio,...) found within real media. Especially, a new output designation has to be derived in a sensible way, which largely depends on how the original output designation has been specified.
+An entire sequence can be embedded within another sequence as a VirtualClip. While for a simple clip there is a Clip-~MObject placed into the model, holding an reference to a media asset, in case of a virtual clip an BindingMO takes on the role of the clip object. Note that, within another context, BindingMO also acts as [[Timeline]] implementation &mdash; indeed even the same sequence might be bound simultaneously as a timeline and into a virtual clip. This flexibility creates a special twist, as the contents of this sequence have no way of finding out if they're used as timeline or embedded virtual clip. So parts of this sequence might mention a OutputDesignation which, when using the sequence as virtual clip, needs to be translated into a virtual media channel, which can be treated in the same manner like any channel (video, audio,...) found within real media. Especially, a new output designation has to be derived in a sensible way, which largely depends on how the original output designation has been specified.
 &rarr; see OutputMapping regarding details and implementation of this mapping mechanism
 
 
@@ -3205,7 +3205,7 @@ An entire sequence can be embedded within another sequence as a VirtualClip. Whi
 
 !Output designation definition
 OutputDesignation is a handle, denoting a target [[Pipe]] to connect.
-It exposes a function to resolve to a Pipe, and to retrieve the StreamType of that resolved output. It can be ''defined'' either explicitly by ~Pipe-ID, or by an indirect or relative specification. The later cases are resolved on demand only (which may be later and might change the meaning depending on the context). It's done this way intentionally to gain flexibility and avoid hard wiring (=explicit ~Pipe-ID)
+It exposes a function to resolve to a Pipe, and to retrieve the StreamType of that resolved output. It can be ''defined'' either explicitly by ~Pipe-ID, or by an indirect or relative specification. The later cases are resolved on demand only (which may be later and might even change the meaning, depending on the context). It's done this way intentionally to gain flexibility and avoid hard wiring (=explicit ~Pipe-ID)
 
 !!!Implementation notes
 Thus the output designation needs to be a copyable value object, but opaque beyond that. Mandated by the various ways to specify an output designation, a hidden state arises regarding the partial resolution. The implementation solves that dilemma by relying on the [[State|http://en.wikipedia.org/wiki/State_pattern]] pattern in combination with an opaque in-place buffer.
@@ -3216,10 +3216,10 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
 * first of all, we need to know //what to route// -- kind of the traits of the data. This is given by the //current pipe.//
 * then we'll need to determine an output designation //suitable for this data.// This is given by a "Plug" (WiringRequest) in the placement, and may be derived.
 * finally, this output designation will be //resolved// -- at least partially, resulting in a target pipe to be used for the wiring
-As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
+As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3236,9 +3236,9 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and &mdash; at the same time &mdash; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
+[[Output designations|OutputDesignation]] are created using a [[Pipe]]-ID and &mdash; they become real by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
 
-We should note that in both cases this mapping operation is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
+We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
 !Connection to external outputs
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
@@ -3250,14 +3250,14 @@ Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputMa
 While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager''. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities.
 
-
-
The term &raquo;''Output Manager''&laquo; might deonte two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global OutputManager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
+
+
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
 
 &rarr; see [[output management overview|OutputManagement]]
 &rarr; see OutputSlot
 &rarr; see ViewerPlayConnection
-
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
 !Mapping situations
@@ -3284,9 +3284,36 @@ All these mapping steps are listed here, because they exhibit a common pattern.
 * there is an //unconnected//&nbsp; state.
 
 !Implementation notes
-Thus the mapping is a copyable value object, based on a associative array. It may be attached to a model object and persisted alongside. The mapping is assumed to run a defaults query when necessary. To allow for that, it should be configured with a query template (string). Frequently, special //default pipe// markers will be used at places, where no distinct pipe-ID is explicitly specified. Besides that, invocations might supply additional predicates (e.g. {{{ord(2)}}} to denote "the second stream of this kind") to hint the defaults resolution. Moreover, the mapping needs a way to retrieve the set of possible results, allowing to filter the results of the rules based default. Mappings might be defined explicitly. Instead of storing a //bottom value,// an {{{isDefined()}}} predicate might be preferable.
+Thus the mapping is a copyable value object, based on a associative array. It may be attached to a model object and persisted alongside. The mapping is assumed to run a defaults query when necessary. To allow for that, it should be configured with a query template (string). Frequently, special //default pipe// markers will be used at places where no distinct pipe-ID is specified explicitly. Besides that, invocations might supply additional predicates (e.g. {{{ord(2)}}} to point at "the second stream of this kind") thereby hinting the defaults resolution. Moreover, the mapping needs a way to retrieve the set of possible results, allowing to filter the results of the rules based default. Mappings might be defined explicitly. Instead of storing a //bottom value,// an {{{isDefined()}}} predicate might be preferable.
 
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
+
+
+
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
+This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
+
+!Properties of an output slot
+Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). In some cases, this explicit registration may be extened to a //factory for additional output slots// -- to be used on demand. An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
+
+In order to be usable as //output sink,// an output slot needs to be allocated and locked. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
+
+Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The size and other characteristics of the data frames is assumed to be suitable. Typically this won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
+
+!!!data exchange models
+Data is handed over by the client invoking an {{{emit(time,...)}}} function on the sink handle. There are two different models how this data hand-over might be performed. On allocation of a slot, the client has to commit to one of them, allowing the output slot to adapt accordingly
+;buffer handover model
+:the client owns the data buffer and cares for allocation and de-allocation. The {{{emit()}}}-call just propagates a pointer to the buffer holding the data ready for output. The output slot implementation in turn has the liability to copy or otherwise use this data within a given time limit.
+;shared buffer model
+:here the output mechanism owns the buffer. Within a certain time window prior to the expected time of the {{{emit()}}}-call, the client may obtain this buffer (pointer) to fill in the data. The slot implementation won't touch this buffer until the {{{emit()}}} handover, which in this case just provides the time and states that the client is done with that buffer. If the data emitting handshake doesn't happen at all, it counts as late and superseded by the next handshake.
+
+!!!timing expectations
+Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These timings are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, latency means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
+
+The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is a job running within the engine scheduler and thus can be configured to run within certain limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
+
+{{red{TODO 6/11}}}in this spec, both data exchange models exhibit a weakness regarding the releasing of buffers. At which time is it safe to release a buffer, when the handover didn't happen? Do we need an explicit callback, and how could this callback be triggered? This is similar to the problem of closing a network connection, i.e. the problem is generally unsolvable, but can be handled pragmatically within certain limits.
+
 
From 4ece1352572902de34e548bf3f3022a60583c60e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 18 Jun 2011 03:34:27 +0200 Subject: [PATCH 041/296] cont.. drafting OutputSlot --- src/proc/play/output-manager.hpp | 18 +----- src/proc/play/output-slot.cpp | 48 +++++++++++++++ src/proc/play/output-slot.hpp | 101 +++++++++++++++++++++++++++++++ wiki/renderengine.html | 18 +++--- 4 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 src/proc/play/output-slot.cpp create mode 100644 src/proc/play/output-slot.hpp diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index df3b4e216..a6fd3b91a 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -32,6 +32,7 @@ #include "lib/error.hpp" +#include "proc/play/output-slot.hpp" #include //#include @@ -50,23 +51,6 @@ namespace play { - /******************************************************************** - * Interface: Generic output sink. - * - * @todo write type comment - */ - class OutputSlot - : boost::noncopyable - { - public: - virtual ~OutputSlot(); - - private: - - }; - -//typedef lib::ScopedPtrVect DisplayerTab; - /****************************************************** diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp new file mode 100644 index 000000000..ff2798ee0 --- /dev/null +++ b/src/proc/play/output-slot.cpp @@ -0,0 +1,48 @@ +/* + OutputSlot - capability to transfer data to a physical output + + Copyright (C) Lumiera.org + 2011, 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/play/output-slot.hpp" + + + +namespace proc { +namespace play { + + + + namespace { // hidden local details of the service implementation.... + + } // (End) hidden service impl details + + + + OutputSlot::~OutputSlot() { } // emit VTables here.... + + + + /** */ + + + + +}} // namespace proc::play diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp new file mode 100644 index 000000000..bf74ff6ed --- /dev/null +++ b/src/proc/play/output-slot.hpp @@ -0,0 +1,101 @@ +/* + OUTPUT-SLOT.hpp - capability to transfer data to a physical output + + Copyright (C) Lumiera.org + 2011, 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 output-slot.hpp + ** An (abstract) capability to send media data to an external output. + ** OutputSlot is the central metaphor for the organisation of actual (system level) outputs; + ** using this concept allows to separate and abstract the data calculation and the organisation + ** of playback and rendering from the specifics of the actual output sink. Actual output + ** possibilities can be added and removed dynamically from various components (backend, GUI), + ** all using the same resolution and mapping mechanisms + ** + ** @see output-test-slot.hpp ////TODO + */ + + +#ifndef PROC_PLAY_OUTPUT_SLOT_H +#define PROC_PLAY_OUTPUT_SLOT_H + + +#include "lib/error.hpp" +#include "lib/handle.hpp" +#include "lib/time/timevalue.hpp" +//#include "lib/sync.hpp" + +#include +//#include +//#include +//#include +//#include + + +namespace proc { +namespace play { + +//using std::string; +//using std::vector; +//using std::tr1::shared_ptr; +//using boost::scoped_ptr; + + + + class Connection; + + class BufferHandoverSink + : public lib::Handle + { + + public: + void emit(Time); + }; + + + class SharedBufferSink + : public lib::Handle + { + + public: + void emit(Time); + }; + + + + + /******************************************************************** + * Interface: Generic output sink. + * + * @todo write type comment + */ + class OutputSlot + : boost::noncopyable + { + public: + virtual ~OutputSlot(); + + private: + + }; + + + +}} // namespace proc::play +#endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index f86c6087b..e35b52c9e 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3219,7 +3219,7 @@ While actually data frames are //pulled,// on a conceptual level data is assumed As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3244,10 +3244,12 @@ We should note that in both cases this [[mapping operation|OutputMapping]] is co
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
 Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
-&rarr; see also the PlayerDummy
+&rarr; see also the PlayService
 
 !the global output manager
-While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager''. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities.
+While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
+
+The relation between the central OutputDirector and the peripheral OutputManger implementations is hierarchical. Because outpust slots are usually registered at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. {{red{Question: is it possible to retrieve a slot from another peripheral node?}}}
 
@@ -3257,13 +3259,13 @@ While mostly the model just denotes the destination for output as OutputDesignat &rarr; see OutputSlot &rarr; see ViewerPlayConnection
-
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
 !Mapping situations
 ;external outputs
 :external outputs aren't addressed directly. Rather we set up a default (channel) mapping, which then can be overridden by local rules.
-:Thus, in this case we query with an internal OutputDesignation as parameter and expect an OutputManager //slot//
+:Thus, in this case we query with an internal OutputDesignation as parameter and expect an OutputSlot
 ;viewer connections
 :any Timeline produces a number of [[model ports|ModelPort]]. On the other hand, any viewer exposes a small number of output designations, representing the image and sound output(s).
 :Thus, in this case we resolve similar to a bus connection, possibly overridden by already pre-existing or predefined connections.
@@ -3289,14 +3291,14 @@ Thus the mapping is a copyable value object, based on a associative array. It ma
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
 !Properties of an output slot
 Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). In some cases, this explicit registration may be extened to a //factory for additional output slots// -- to be used on demand. An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
 
-In order to be usable as //output sink,// an output slot needs to be allocated and locked. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
+In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
 
 Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The size and other characteristics of the data frames is assumed to be suitable. Typically this won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
 
@@ -3308,7 +3310,7 @@ Data is handed over by the client invoking an {{{emit(time,...)}}} function on t
 :here the output mechanism owns the buffer. Within a certain time window prior to the expected time of the {{{emit()}}}-call, the client may obtain this buffer (pointer) to fill in the data. The slot implementation won't touch this buffer until the {{{emit()}}} handover, which in this case just provides the time and states that the client is done with that buffer. If the data emitting handshake doesn't happen at all, it counts as late and superseded by the next handshake.
 
 !!!timing expectations
-Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These timings are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, latency means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
+Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These timings are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, &raquo;latency&laquo; means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
 
 The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is a job running within the engine scheduler and thus can be configured to run within certain limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
 

From a19562942cf48ed68ab084caf6bc520f2464ffd7 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 21 Jun 2011 04:24:01 +0200
Subject: [PATCH 042/296] further considerations regarding output and output
 slots

---
 src/include/play-facade.h     |  4 ++--
 src/lib/streamtype.hpp        |  7 ++++++-
 src/proc/play/output-slot.hpp |  9 ++++++---
 wiki/renderengine.html        | 32 ++++++++++++++++++++++----------
 4 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/src/include/play-facade.h b/src/include/play-facade.h
index 84865444c..852fe5b78 100644
--- a/src/include/play-facade.h
+++ b/src/include/play-facade.h
@@ -115,8 +115,8 @@ namespace lumiera {
           };
         
         
-        typedef lib::IterSource&    ModelPorts;
-        typedef lib::IterSource& Pipes;
+        typedef lib::IterSource::iterator    ModelPorts;
+        typedef lib::IterSource::iterator Pipes;
         typedef proc::play::POutputManager Output;
         typedef mobject::session::PClipMO Clip;
         typedef mobject::PTrack  Track;
diff --git a/src/lib/streamtype.hpp b/src/lib/streamtype.hpp
index 745f50d86..e344b811d 100644
--- a/src/lib/streamtype.hpp
+++ b/src/lib/streamtype.hpp
@@ -110,7 +110,12 @@ namespace lumiera {
       
       class TypeTag ; 
       
-      /** placeholder definition for the contents of a data buffer */
+      /** 
+       * placeholder type for the contents of a data buffer.
+       * The actual buffer will always be provided by a 
+       * library implementation; throughout the engine,
+       * it's just hidden behind a DataBuffer pointer.
+       */
       struct DataBuffer { };
       
       
diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp
index bf74ff6ed..2b7a1e754 100644
--- a/src/proc/play/output-slot.hpp
+++ b/src/proc/play/output-slot.hpp
@@ -39,6 +39,7 @@
 #include "lib/error.hpp"
 #include "lib/handle.hpp"
 #include "lib/time/timevalue.hpp"
+#include "proc/engine/buffhandle.hpp"
 //#include "lib/sync.hpp"
 
 #include 
@@ -50,14 +51,16 @@
 
 namespace proc {
 namespace play {
-  
+
+  using ::engine::BuffHandle;
 //using std::string;
+
 //using std::vector;
 //using std::tr1::shared_ptr;
 //using boost::scoped_ptr;
   
   
-  
+  /** established output channel */
   class Connection;
   
   class BufferHandoverSink
@@ -65,7 +68,7 @@ namespace play {
     {
       
     public:
-      void emit(Time);
+      void emit(Time, BuffHandle);
     };
   
   
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index e35b52c9e..292e26c24 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1774,8 +1774,10 @@ To make the intended use of the classes more clear, consider the following two e
 * a video clip placed relatively, with an attached HUE effect &rarr;[[Example2]]
 
-
+
a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
+
+The render nodes network is always buils separate for each [[timeline segment|Segmentation]], which is //constant in wiring configuration.// Thus, while exit node(s) are per segment, the corresponding exit nodes of consecutive segments together belong to a ModelPort, which in turn corresponds to a global pipe (master bus not connected any further). These relations guide the possible configuration for an exit node: It may still provide multiple channels -- but all those channels are bound to belong to a single logical stream -- same StreamPrototype, always handled as bundle, connected and routed in one step. For example, when there is an 5.1 Audio master bus with a single fader, then "5.1 Audio" would be a prototype and these 6 channels will always be handled together; in such a case it makes perfectly sense to access these 6 audio channels through a single exit node, which is keyed (identified) by the same [[Pipe]]-ID as the corresponding ModelPort and the corresponding global pipe ("5.1 Audio master bus")
 
@@ -3219,7 +3221,7 @@ While actually data frames are //pulled,// on a conceptual level data is assumed As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3236,7 +3238,7 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created using a [[Pipe]]-ID and &mdash; they become real by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
+[[Output designations|OutputDesignation]] are created using a [[Pipe]]-ID and &mdash; they become real by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of [[model ports|ModelPort]] as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
 
 We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
@@ -3244,20 +3246,30 @@ We should note that in both cases this [[mapping operation|OutputMapping]] is co
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
 Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
+&rarr; the OutputManager interface describes handling this mapping association
 &rarr; see also the PlayService
 
 !the global output manager
-While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
+While within the model routing is done mostly just by referring to an OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
 
-The relation between the central OutputDirector and the peripheral OutputManger implementations is hierarchical. Because outpust slots are usually registered at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. {{red{Question: is it possible to retrieve a slot from another peripheral node?}}}
+The relation between the central OutputDirector and the peripheral OutputManager implementations is hierarchical. Because output slots are usually registered rather at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. Resolving a global pipe into an output slot is the core concern of any OutputManager implementation. Thus, when there is a locally preconfigured mapping, like e.g. for a viewer's video master pipe to the output slot installed by the corresponding GUI viewer element, then this mapping will picked up foremost to resolve the video master output.
+
+For a viewer widget in the GUI this yields exactly the expeted behaviour, but in other cases, e.g. for sound output, we need more general, more globally scoped output slots. In these cases, when a local mapping is absent, the query for output resolution is passed on up to the  OutputDirector, drawing on the collection of globally available output slots for that specific kind of media.
+{{red{Question: is it possible to retrieve a slot from another peripheral node?}}}
 
-
-
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
+
+
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, the OutputDirector, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
 
 &rarr; see [[output management overview|OutputManagement]]
 &rarr; see OutputSlot
-&rarr; see ViewerPlayConnection
+&rarr; see ViewerPlayConnection + +!Role of an output manager +The output manager interface describes an entity handling two distinct concerns, tied together within a local //scope.// +* a ''mapping'' to resolve a ModelPort (given as ~Pipe-ID) into an OutputSlot (or several in case of multichannel media) +* the ''registration'' and management of possible output slots, thereby creating a preferred local mapping for future connections +
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
@@ -5427,7 +5439,7 @@ if (oldText.indexOf("SplashScreen")==-1)
 * 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. 
 
 Prototypes form an open (extensible) collection, though each prototype belongs to a specific media kind ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}).
@@ -5440,7 +5452,7 @@ Consequently, as we can't get away with an fixed Enum of all stream prototypes,
 * 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 linear PCM...
+In practice, several things might be considered "quite different" and thus be distinguished by protorype: 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...
 
From dea1fa57a21ac2b0478b763cf45532bf6a647685 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 22 Jun 2011 02:42:50 +0200 Subject: [PATCH 043/296] draft play process structure; clarify handling of multiple channels --- doc/devel/uml/fig143877.png | Bin 0 -> 13790 bytes doc/devel/uml/fig144005.png | Bin 0 -> 15144 bytes src/proc/engine/buffhandle.hpp | 16 +- src/proc/engine/nodeinvocation.hpp | 1 + src/proc/engine/nodeoperation.hpp | 2 +- src/proc/play/output-slot.hpp | 23 ++ uml/lumiera/129285 | 30 +-- uml/lumiera/133637 | 348 +++++++++++++++++++++++++++++ uml/lumiera/143877.diagram | 88 ++++++++ uml/lumiera/144005.diagram | 94 ++++++++ uml/lumiera/5.session | 39 +--- uml/lumiera/lumiera.prj | 2 +- wiki/renderengine.html | 34 ++- 13 files changed, 607 insertions(+), 70 deletions(-) create mode 100644 doc/devel/uml/fig143877.png create mode 100644 doc/devel/uml/fig144005.png create mode 100644 uml/lumiera/133637 create mode 100644 uml/lumiera/143877.diagram create mode 100644 uml/lumiera/144005.diagram diff --git a/doc/devel/uml/fig143877.png b/doc/devel/uml/fig143877.png new file mode 100644 index 0000000000000000000000000000000000000000..1e628bc4158d075b837cc675779831ed96881bc6 GIT binary patch literal 13790 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 literal 0 HcmV?d00001 diff --git a/doc/devel/uml/fig144005.png b/doc/devel/uml/fig144005.png new file mode 100644 index 0000000000000000000000000000000000000000..93627a5ac9bd816faa12a234164a0fdbff947f54 GIT binary patch literal 15144 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 literal 0 HcmV?d00001 diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 6cdca586c..0f6378396 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -52,6 +52,9 @@ namespace engine { /** * Handle for a buffer for processing data, abstracting away the actual implementation. * The real buffer pointer can be retrieved by dereferencing this smart-handle class. + * + * @todo as of 6/2011 it isn't clear how buffer handles are actually created + * and how the lifecycle (and memory) management works */ struct BuffHandle : lib::BoolCheckable @@ -78,15 +81,24 @@ namespace engine { } - //////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> Ticket 249 + //////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> TICKET #249 BuffHandle() : pBuffer_(0), sourceID_(0) { } + /** + * @deprecated placeholder implementation + * @todo rework the Lifecycle handling of buffers //////////TICKET #249 + */ + BuffHandle(PBuff existingBuffer) + : pBuffer_(existingBuffer) + , sourceID_(0) + { } + private: PBuff pBuffer_; - long sourceID_; + long sourceID_; ////TICKET #249 this is a placeholder for a "type-like information", to be used for lifecycle management and sanity checks.... }; diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp index dd9b17ea4..ad9c6d402 100644 --- a/src/proc/engine/nodeinvocation.hpp +++ b/src/proc/engine/nodeinvocation.hpp @@ -172,6 +172,7 @@ namespace engine { }; + ////////////TICKET #249 this strategy should better be hidden within the BuffHanle ctor (and type-erased after creation) struct AllocBufferFromParent ///< using the parent StateAdapter for buffer allocations : Invocation { diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index ce587862b..63753cee6 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -245,7 +245,7 @@ namespace config { template struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor?? - { ///////////////// this would require BuffHandle to be a smart ref.... + { ///////////////// this would require BuffHandle to be a smart ref.... --> ///TICKET #249 BuffHandle step (Invocation& ivo) { diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 2b7a1e754..36f6de9b7 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -40,6 +40,7 @@ #include "lib/handle.hpp" #include "lib/time/timevalue.hpp" #include "proc/engine/buffhandle.hpp" +#include "lib/iter-source.hpp" //#include "lib/sync.hpp" #include @@ -53,6 +54,7 @@ namespace proc { namespace play { using ::engine::BuffHandle; + using lib::time::Time; //using std::string; //using std::vector; @@ -94,6 +96,27 @@ namespace play { public: virtual ~OutputSlot(); + typedef lib::IterSource::iterator Opened_BufferHandoverSinks; + typedef lib::IterSource::iterator Opened_SharedBufferSinks; + + template + struct Allocation + { + typedef typename lib::IterSource::iterator OpenedSinks; + + OpenedSinks getOpenedSinks(); + + bool isActive(); + + /////TODO add here the getters for timing constraints + }; + + + bool isFree() const; + + Allocation + allocate(); + private: }; diff --git a/uml/lumiera/129285 b/uml/lumiera/129285 index 117e269b7..bb1c705fe 100644 --- a/uml/lumiera/129285 +++ b/uml/lumiera/129285 @@ -1,6 +1,6 @@ format 58 "ProcessingLayer" // ProcessingLayer - revision 26 + revision 27 modified_by 5 "hiv" // class settings //class diagram settings @@ -30,6 +30,8 @@ format 58 package_ref 129029 // Control + package_ref 133637 // Play + package_ref 128261 // MObject package_ref 128389 // RenderEngine @@ -330,8 +332,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 131333 // ouput end end @@ -350,8 +350,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 131589 // end end @@ -360,8 +358,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 131717 // vid_a end end @@ -370,8 +366,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 131461 // input end end @@ -430,8 +424,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 133893 // HUE end end @@ -440,8 +432,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 134021 // vid_a end end @@ -464,8 +454,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 133253 // HUE end end @@ -474,8 +462,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 132869 // input end end @@ -506,8 +492,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 133125 // ouput end end @@ -516,8 +500,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 132613 // devnull end end @@ -534,8 +516,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 134021 // vid_a end end @@ -558,8 +538,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 134149 // input end end @@ -568,8 +546,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 134405 // ouput end end diff --git a/uml/lumiera/133637 b/uml/lumiera/133637 new file mode 100644 index 000000000..d788a91a9 --- /dev/null +++ b/uml/lumiera/133637 @@ -0,0 +1,348 @@ +format 58 +"Play" // ProcessingLayer::Play + revision 1 + modified_by 5 "hiv" + // class settings + //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_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default class_drawing_mode default shadow default show_stereotype_properties 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 show_stereotype_properties 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 show_stereotype_properties 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 show_stereotype_properties default + //component diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + draw_component_as_icon default show_component_req_prov default show_component_rea default show_stereotype_properties default + //deployment diagram settings + package_name_in_tab default show_context default write_horizontally default auto_label_position default draw_all_relations default shadow default + draw_component_as_icon default show_component_req_prov default show_component_rea default show_stereotype_properties 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 show_stereotype_properties default + //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 show_stereotype_properties default + + classview 136837 "PlayOut" + //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_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties 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 show_stereotype_properties 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 show_stereotype_properties 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 show_stereotype_properties 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 show_stereotype_properties 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 show_stereotype_properties default + classdiagram 143877 "Player Entities" + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + size A4 + end + + objectdiagram 144005 "Play Process Structure" + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default show_stereotype_properties default + size A4 + end + + class 176133 "OutputSlot" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 176261 "Controller" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 176389 "PlayProcess" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 216837 // + relation 205701 *--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type} ${name}${value}; +" + classrelation_ref 216837 // + b parent class_ref 177285 // Feed + end + end + + class 176517 "OutputManager" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 216709 // + relation 205573 o--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 216709 // + b parent class_ref 176133 // OutputSlot + end + end + + class 176645 "OutputDirector" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 216581 // + relation 205445 -_-|> + a public + cpp default "${type}" + classrelation_ref 216581 // + b parent class_ref 176517 // OutputManager + end + end + + class 176773 "ModelPort" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 176901 "CalcStream" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 217093 // + relation 205957 ---> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 217093 // + b parent class_ref 177029 // Dispatcher + end + end + + class 177029 "Dispatcher" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 177157 "PlayService" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + classinstance 145669 "" + type class_ref 176261 // Controller + attributes + end + relations + end + end + + classinstance 145797 "" + type class_ref 176389 // PlayProcess + attributes + end + relations + end + end + + class 177285 "Feed" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 216965 // + relation 205829 *--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type} ${name}${value}; +" + classrelation_ref 216965 // + b parent class_ref 176901 // CalcStream + end + end + + classinstance 145925 "" + type class_ref 177285 // Feed + attributes + end + relations + end + end + + classinstance 146053 "" + type class_ref 177285 // Feed + attributes + end + relations + end + end + + classinstance 146181 "video" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146309 "video" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146437 "sound-W" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146565 "sound-X" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146693 "sound-Z" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146821 "" + type class_ref 176773 // ModelPort + attributes + end + relations + end + end + + classinstance 146949 "" + type class_ref 176773 // ModelPort + attributes + end + relations + end + end + + classinstance 147077 "" + type class_ref 176133 // OutputSlot + attributes + end + relations + end + end + + classinstance 147205 "" + type class_ref 176133 // OutputSlot + attributes + end + relations + end + end + end +end diff --git a/uml/lumiera/143877.diagram b/uml/lumiera/143877.diagram new file mode 100644 index 000000000..f49ed5f08 --- /dev/null +++ b/uml/lumiera/143877.diagram @@ -0,0 +1,88 @@ +format 58 + +classcanvas 128005 class_ref 176133 // OutputSlot + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 349 106 2000 +end +classcanvas 128133 class_ref 176261 // Controller + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 71 172 2000 +end +classcanvas 128261 class_ref 176389 // PlayProcess + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 143 222 2000 +end +classcanvas 128389 class_ref 176517 // OutputManager + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 472 47 2000 +end +classcanvas 128517 class_ref 176645 // OutputDirector + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 474 165 2000 +end +classcanvas 129541 class_ref 176773 // ModelPort + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 355 266 2000 +end +classcanvas 129669 class_ref 176901 // CalcStream + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 207 380 2000 +end +classcanvas 129797 class_ref 177029 // Dispatcher + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 355 381 2000 +end +fragment 130053 "output management" + xyzwh 299 17 2000 324 218 +end +fragment 130181 "Model / Fixture" + xyzwh 299 239 1995 323 91 +end +fragment 130309 "Player" + xyzwh 16 87 1990 274 243 +end +classcanvas 130437 class_ref 177157 // PlayService + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 40 118 2005 +end +fragment 130565 "Engine" + xyzwh 16 346 1995 606 208 +end +classcanvas 130693 class_ref 177285 // Feed + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 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 show_stereotype_properties default + xyz 218 273 2000 +end +relationcanvas 128645 relation_ref 205445 // + from ref 128517 z 1999 to ref 128389 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 128773 relation_ref 205573 // + geometry HVH + from ref 128389 z 1999 to point 439 64 + line 129285 z 1999 to point 439 123 + line 129413 z 1999 to ref 128005 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 130821 relation_ref 205701 // + geometry VH + from ref 128261 z 1999 to point 178 290 + line 130949 z 1999 to ref 130693 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 131077 relation_ref 205829 // + from ref 130693 z 1999 to ref 129669 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 131205 relation_ref 205957 // + from ref 129669 z 1999 to ref 129797 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +line 131333 -_-_ geometry VH + from ref 128133 z 1999 to point 99 239 + line 131461 z 1999 to ref 128261 +end diff --git a/uml/lumiera/144005.diagram b/uml/lumiera/144005.diagram new file mode 100644 index 000000000..42a499609 --- /dev/null +++ b/uml/lumiera/144005.diagram @@ -0,0 +1,94 @@ +format 58 + +classinstancecanvas 128005 classinstance_ref 145669 // + xyz 41 85 2000 +end +classinstancecanvas 128133 classinstance_ref 145797 // + xyz 105 142 2000 +end +classinstancecanvas 128261 classinstance_ref 145925 // + xyz 171 173 2000 +end +classinstancecanvas 128389 classinstance_ref 146053 // + xyz 171 240 2000 +end +classinstancecanvas 128517 classinstance_ref 146181 // video + xyz 233 194 2000 color lightgreen +end +classinstancecanvas 128645 classinstance_ref 146309 // video + xyz 233 216 2000 color lightgreen +end +classinstancecanvas 128773 classinstance_ref 146437 // sound-W + xyz 233 269 2000 color lightgreen +end +classinstancecanvas 128901 classinstance_ref 146565 // sound-X + xyz 233 291 2000 color lightgreen +end +classinstancecanvas 129029 classinstance_ref 146693 // sound-Z + xyz 233 313 2000 color lightgreen +end +classinstancecanvas 129157 classinstance_ref 146693 // sound-Z + xyz 233 335 2000 color lightgreen +end +classinstancecanvas 132101 classinstance_ref 146821 // + xyz 420 269 2000 color lightblue +end +classinstancecanvas 132229 classinstance_ref 146949 // + xyz 420 194 2000 color lightblue +end +note 132357 "stateless (functional)" + xyzwh 348 140 2000 124 35 +note 132485 "stateful" + color verylightorange xyzwh 131 85 2000 72 36 +classinstancecanvas 132613 classinstance_ref 147077 // + xyz 233 23 2000 +end +classinstancecanvas 132741 classinstance_ref 147205 // + xyz 233 46 2000 +end +objectlinkcanvas 129285 norel + geometry VH + from ref 128133 z 1999 to point 139 181 + line 129413 z 1999 to ref 128261 + no_role_a no_role_b +objectlinkcanvas 129541 norel + geometry VH + from ref 128133 z 1999 to point 139 248 + line 129797 z 1999 to ref 128389 + no_role_a no_role_b +objectlinkcanvas 129925 norel + geometry VH + from ref 128261 z 1999 to point 194 202 + line 130053 z 1999 to ref 128517 + no_role_a no_role_b +objectlinkcanvas 130181 norel + geometry VH + from ref 128261 z 1999 to point 194 224 + line 130309 z 1999 to ref 128645 + no_role_a no_role_b +objectlinkcanvas 130437 norel + geometry VH + from ref 128389 z 1999 to point 194 277 + line 130565 z 1999 to ref 128773 + no_role_a no_role_b +objectlinkcanvas 130693 norel + geometry VH + from ref 128389 z 1999 to point 194 299 + line 130821 z 1999 to ref 128901 + no_role_a no_role_b +objectlinkcanvas 130949 norel + geometry VH + from ref 128389 z 1999 to point 194 321 + line 131077 z 1999 to ref 129029 + no_role_a no_role_b +objectlinkcanvas 131205 norel + geometry VH + from ref 128389 z 1999 to point 194 343 + line 131333 z 1999 to ref 129157 + no_role_a no_role_b +objectlinkcanvas 131845 norel + geometry VH + from ref 128005 z 1999 to point 68 150 + line 131973 z 1999 to ref 128133 + no_role_a no_role_b +end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 4e22d4c88..0e80581b8 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -4,12 +4,10 @@ diagrams 631 352 100 4 0 0 objectdiagram_ref 138885 // ModelAssetRelations 730 488 100 4 0 0 - classdiagram_ref 142725 // Time flavours - 595 646 100 4 0 0 - classdiagram_ref 131205 // Struct-Asset Relations - 530 608 100 4 0 0 - active classdiagram_ref 130309 // Asset Kinds - 855 805 100 4 0 0 + classdiagram_ref 143877 // Player Entities + 663 648 100 4 0 0 + active objectdiagram_ref 144005 // Play Process Structure + 562 424 100 4 0 0 end show_stereotypes selected @@ -17,36 +15,13 @@ selected open package_ref 128005 // design - class_ref 160389 // VirtualMedia - class_ref 136837 // Proc - class_ref 152197 // Sequence - class_ref 160901 // Timeline - class_ref 174981 // Viewer - class_ref 139269 // DoRecurse - class_ref 162821 // TypedID::Link classview_ref 128389 // Controller Workings - class_ref 139653 // Session - class_ref 145541 // Timeline - class_ref 128133 // Seq - class_ref 160517 // Root - class_ref 128389 // Track - class_ref 152325 // Binding - class_ref 129797 // ExplicitPlacement - class_ref 129029 // Effect - class_ref 139909 // LocatingPin - class_ref 152453 // PlacementRef - classrelation_ref 178437 // + classview_ref 136837 // PlayOut class_ref 153733 // QueryFocusStack usecaseview_ref 128261 // config examples package_ref 128389 // RenderEngine - classdiagram_ref 142725 // Time flavours - class_ref 134917 // Time - class_ref 168837 // Duration - class_ref 169221 // TimeGrid - class_ref 170373 // TimeVar - class_ref 170501 // QuTimeSpan - classview_ref 128645 // Service Components - classview_ref 128266 // SmartPointers + + package_ref 129157 // BackendLayer end end diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index 35fe3a3e2..e9189265f 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 58 "lumiera" - revision 69 + revision 70 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 292e26c24..927c2b79b 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3037,7 +3037,7 @@ __Note__: nothing within the PlacementIndex requires the root object to be of a
-
+
Based on practical experiences, Ichthyo tends to consider Multichannel Media as the base case, while counting media files providing just one single media stream as exotic corner cases. This may seem counter intuitive at first sight; you should think of  it as an attempt to avoid right from start some of the common shortcomings found in many video editors, especially
 * having to deal with keeping a "link" between audio and video clips
 * silly limitations on the supported audio setups (e.g. "sound is mono, stereo or Dolby-5.1")
@@ -3070,6 +3070,12 @@ While the general approach and reasoning remains valid, a lot of the details loo
 * while the asset related parts remain as specified, we get a distinct ChannelConfig asset instead of the clip asset (which seems to be redundant)
 * either the ~ClipMO referres this ChannelConfig asset &mdash; or in case of the VirtualClip a BindingMO takes this role. Clip Asset and MO could be joined into a single entity
 * as the BindingMO is also used to implement the top-level timelines, the treatment of global and local pipes is united
+* every pipe (bus) should be able to carry multiple channels, //but with the limitation to only a single media StreamType//
+* this "multichannel-of-same-kind" capability carries over to the ModelPort entries and even the OutputSlot elements
+* only when allocating / "opening" an OutputSlot, we get multiple handles for plain single channels.
+* this can be considered the breaking point, where we enter the realm of the render engine. Here, indeed, only single channels are processed
+
+
 
@@ -3258,7 +3264,7 @@ For a viewer widget in the GUI this yields exactly the expeted behaviour, but in {{red{Question: is it possible to retrieve a slot from another peripheral node?}}}
-
+
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, the OutputDirector, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
 
 &rarr; see [[output management overview|OutputManagement]]
@@ -3267,8 +3273,12 @@ For a viewer widget in the GUI this yields exactly the expeted behaviour, but in
 
 !Role of an output manager
 The output manager interface describes an entity handling two distinct concerns, tied together within a local //scope.//
-* a ''mapping'' to resolve a ModelPort (given as ~Pipe-ID) into an OutputSlot (or several in case of multichannel media)
+* a ''mapping'' to resolve a ModelPort (given as ~Pipe-ID) into an OutputSlot
 * the ''registration'' and management of possible output slots, thereby creating a preferred local mapping for future connections
+
+Note that an OutputSlot acts as a unit for registration and also for allocating / "opening" an output, while generally there might still be multiple physical outputs grouped into a single slot. This is relevant especially for sound output. A single slot is just the ability to allocate output ports up to a given limit (e.g. 2 for a stereo device, or 6 for a 5.1 device). These multiple channels are allways connected following a natural channel ordering. Thus the mapping is a simple 1:1 association from pipe to slot, assuming that the media types are compatible (and this has been checked already on a higher level).
+
+The //registration//&nbsp; of an output slot installs a functor or association rule, which later on allows to claim and connect up to a preconfigured number of channels. This allocation or usage of a slot is exclusive (i.e. only a single client at a time can allocate a slot, even if not using all the possible channels). Each output manager instance may or may not be configured with a //fall-back rule:// when no association or mapping can be established locally, the connection request might be passed down to the global OutputDirector. Again, we can expect this to be the standard behaviour for sound, while video likely will rather be handled locally, e.g. within a GUI widget (but we're not bound to configure it exactly this way)
 
@@ -3303,12 +3313,12 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
 !Properties of an output slot
-Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). In some cases, this explicit registration may be extened to a //factory for additional output slots// -- to be used on demand. An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
+Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, or a group of such outputs (e.g. left and right soundcard output channels), or an output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
 
 In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
 
@@ -3328,6 +3338,9 @@ The assumption is for the client to have elaborate timing capabilities at his di
 
 {{red{TODO 6/11}}}in this spec, both data exchange models exhibit a weakness regarding the releasing of buffers. At which time is it safe to release a buffer, when the handover didn't happen? Do we need an explicit callback, and how could this callback be triggered? This is similar to the problem of closing a network connection, i.e. the problem is generally unsolvable, but can be handled pragmatically within certain limits.
 
+!!!Lifecycle and storage
+The concrete OutputSlot implementation is owned and managed by the facility actually providing this output possibility. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. This implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be deregistered explicitly prior to destruction -- and this deregistration may block until all clients using this slot are terminated. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferrably just emitting a signal (callback functor).
+
 
@@ -4188,14 +4201,21 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T <<tasksum end>>
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
 The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
 ''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalculationStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
 * the [[Backend]] is responsible for scheduling and [[dispatching|Dispatcher]] the CalculationStream
 * the RenderEngine has the ability to cary out individual frame calculations
-* the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
+* the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery + +[>img[Anatomy of a Play Process|uml/fig144005.png]] +!Anatomy of a Play Process +The Controller is exposed to the client and acts as frontend handle, while the play process body groups and manages all the various parts cooperating to generate output. For each of the participating global pipes we get a feed to drive that pipeline to deliver media of a specific kind. + +Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, hooked up into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalculationStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalculationStream is created, while the existing one is just used to mark any not-yet processed job as superseded. +
The [[Player]] is an independent [[Subsystem]] within Lumiera, located at Proc-Layer level. A more precise term would be "rendering and playback coordination subsystem". It provides the capability to generate media data, based on a high-level model object, and send this generated data to an OutputDesignation, creating an continuous and timing controlled output stream. Clients may utilise these functionality through the ''play service'' interface.

From 9e53053944683e88056a94af9ec24ca67d4e8731 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 23 Jun 2011 17:54:28 +0200
Subject: [PATCH 044/296] WIP: draft building a PlayProcess

---
 src/proc/play/play-process.hpp | 45 ++++++++++++++++++++++++++++++++--
 src/proc/play/play-service.cpp | 33 +++++++++++++++++++++----
 wiki/renderengine.html         | 13 +++++++---
 3 files changed, 81 insertions(+), 10 deletions(-)

diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp
index 9207a3b10..5045544b7 100644
--- a/src/proc/play/play-process.hpp
+++ b/src/proc/play/play-process.hpp
@@ -21,7 +21,27 @@
 */
 
 /** @file play-process.hpp
- *  
+ ** Organisational unit of an ongoing render- or playback process.
+ ** A process object doesn't perform any work in itself, rather it's
+ ** an entry in the process table maintained within the PlayService.
+ ** This table entry is used to keep track of the individual data feeds,
+ ** each corresponding to one of the global pipes to be "performed" in order
+ ** to generate output data. Usually, these global pipes all belong to a given
+ ** Timeline (but other setups are possible as well).
+ ** 
+ ** Each of these Feed objects comprising a play process is in turn responsible
+ ** for getting one or multiple CalculationStream entities configured and operative
+ ** within the actual render engine. Each of these calculation streams corresponds
+ ** to a running series of calculations for consecutive frames, to be delivered
+ ** in a time-bound fashion from the render engine into an OutputSlot, allocated
+ ** for rendering this specific feed.
+ ** 
+ ** A PlayProcess isn't exposed directly to client code -- it's the body object,
+ ** while a Play::Controller handle is returned to the client (PImpl pattern).
+ ** Using this controller frontend, clients are allowed to control and change
+ ** the playback or rendering state and goals, which then causes the PlayProcess
+ ** to reconfigure the ongoing or planned calculations.
+ ** 
  ** @see lumiera::DummyPlayer
  ** @see gui::PlaybackController usage example 
  */
@@ -31,6 +51,7 @@
 #define PROC_PLAY_PLAY_PROCESS_H
 
 
+#include "lib/error.hpp"
 //#include "include/dummy-player-facade.h"
 //#include "include/display-facade.h"
 //#include "common/instancehandle.hpp"
@@ -49,8 +70,21 @@ namespace play {
 //    using lumiera::Display;
 //    using lumiera::DummyPlayer;
   
+  namespace error = lumiera::error;
   
   
+  /**
+   * Rendering data feed, corresponding to a single
+   * global pipe and to be delivered into a single OutputSlot.
+   * A feed may still be comprised of multiple channels, but is
+   * bound to operate on a single type of media data only.
+   */
+  class Feed
+    : boost::noncopyable
+    {
+      
+    };
+  
   /******************************************************
    * Playback/Render process within the Lumiera Player.
    * This is a top-level implementation entity, created
@@ -72,7 +106,14 @@ namespace play {
     {
       
     public:
-      
+      template
+      PlayProcess (CONS pipeConnections)
+        {
+          if (isnil (pipeConnections))
+            throw error::State ("creating a PlayProcess without any usable output connections");
+          
+          UNIMPLEMENTED ("iterate over the connections and allocate/establish each of them, creating and storing Feed objects");
+        }
     };
   
   
diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp
index 50077545a..72e89fa35 100644
--- a/src/proc/play/play-service.cpp
+++ b/src/proc/play/play-service.cpp
@@ -23,12 +23,13 @@
 
 #include "proc/play/play-service.hpp"
 #include "proc/play/play-process.hpp"
-#include "lib/scoped-ptrvect.hpp" 
+#include "lib/itertools.hpp"
 
 
 #include 
 //#include 
-//#include 
+#include 
+#include 
 //#include 
 
 
@@ -53,6 +54,10 @@ namespace play {
 //using std::tr1::bind;
   using lib::Sync;
   using lib::RecursiveLock_NoWait;
+  using lib::filterIterator;
+  using std::tr1::weak_ptr;
+  using std::tr1::bind;
+  using std::tr1::placeholders::_1;
   
   
   namespace { // hidden local details of the service implementation....
@@ -62,8 +67,6 @@ namespace play {
     
     
     
-    /* ================== define an lumieraorg_DummyPlayer instance ======================= */
-    
     
   } // (End) hidden service impl details
   
@@ -71,12 +74,29 @@ namespace play {
   class ProcessTable
     : public Sync
     {
-      typedef lib::ScopedPtrVect ProcTable;
+      typedef std::vector > ProcTable;
       
       ProcTable processes_;
       
     public:
       
+      lumiera::Play::Controller
+      establishProcess (PlayProcess* newProcess)
+        {
+          lumiera::Play::Controller frontend;
+          
+          frontend.activate (newProcess, bind (&ProcessTable::endProcess, this, _1 ));
+          processes_.push_back (frontend);
+          return frontend;
+        }
+      
+    private:
+      void
+      endProcess (PlayProcess* dyingProcess)
+        {
+          delete dyingProcess;
+          UNIMPLEMENTED ("process deregistration");   /// note the process might not be registered at all
+        }
     };
   
   
@@ -114,6 +134,9 @@ namespace play {
   PlayService::connect(ModelPorts dataGenerators, Output outputDestinations)
   {
     UNIMPLEMENTED ("build a PlayProcess");
+    pTable_->establishProcess(
+                 new PlayProcess (filterIterator (dataGenerators,
+                                                  resolve(outputDestinations))));
   }
   
 
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 927c2b79b..ce2cd3f38 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -3313,7 +3313,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3341,6 +3341,13 @@ The assumption is for the client to have elaborate timing capabilities at his di
 !!!Lifecycle and storage
 The concrete OutputSlot implementation is owned and managed by the facility actually providing this output possibility. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. This implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be deregistered explicitly prior to destruction -- and this deregistration may block until all clients using this slot are terminated. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferrably just emitting a signal (callback functor).
 
+-----
+!Implementation / design problems
+How to handle the selection of those two data exchange models!
+-- Problem is, typically this choice isn't up to the client; rather, the concrete OutputSlot implementation dictates what model to use. Yet I wanted to handle these through //generic programming// -- because they can't be subsumed under a single interface without distorting the usage or the structure.
+
+Thus: Client gets an {{{OutputSlot&}}}, without knowing the exact implementation type &rArr; how can the client pick up the right strategy, note, at //compile time!//
+
 
@@ -4201,7 +4208,7 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T <<tasksum end>>
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
 The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
@@ -4214,7 +4221,7 @@ The play process is an conceptual entity linking together several activities in
 !Anatomy of a Play Process
 The Controller is exposed to the client and acts as frontend handle, while the play process body groups and manages all the various parts cooperating to generate output. For each of the participating global pipes we get a feed to drive that pipeline to deliver media of a specific kind.
 
-Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, hooked up into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalculationStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalculationStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
+Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the current collection of individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, wired into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalculationStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalculationStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
 
From 511d08adad4c8f47389319e8e375c2d67980f6d3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 24 Jun 2011 18:40:19 +0200 Subject: [PATCH 045/296] WIP: bilding a PlayProcess... --- src/include/play-facade.h | 2 ++ src/proc/play/output-manager.hpp | 10 +++++- src/proc/play/play-process.hpp | 6 +++- src/proc/play/play-service.cpp | 61 +++++++++++++++++++++++++++----- src/proc/play/play-service.hpp | 1 + 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/include/play-facade.h b/src/include/play-facade.h index 852fe5b78..c1739bac6 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -112,6 +112,8 @@ namespace lumiera { double getSpeed() const; uint getQuality() const; bool usesProxy() const; + + operator std::tr1::weak_ptr(); }; diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index a6fd3b91a..165c1af2f 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -33,6 +33,7 @@ #include "lib/error.hpp" #include "proc/play/output-slot.hpp" +#include "proc/mobject/model-port.hpp" #include //#include @@ -48,6 +49,10 @@ namespace play { using std::tr1::shared_ptr; + LUMIERA_ERROR_DECLARE(CANT_PLAY); ///< unable to build playback or render process for this configuration + + + @@ -62,7 +67,10 @@ namespace play { : boost::noncopyable { public: - OutputManager() {} + virtual ~OutputManager() { } + + + virtual OutputSlot& getOutputFor (mobject::ModelPort port) =0; }; typedef shared_ptr POutputManager; diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 5045544b7..aebd971dd 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -56,6 +56,8 @@ //#include "include/display-facade.h" //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" +#include "proc/play/output-manager.hpp" +#include "lib/util.hpp" // #include //#include @@ -69,6 +71,7 @@ namespace play { // using lumiera::Subsys; // using lumiera::Display; // using lumiera::DummyPlayer; + using util::isnil; namespace error = lumiera::error; @@ -110,7 +113,8 @@ namespace play { PlayProcess (CONS pipeConnections) { if (isnil (pipeConnections)) - throw error::State ("creating a PlayProcess without any usable output connections"); + throw error::State ("creating a PlayProcess without any usable output connections" + , LUMIERA_ERROR_CANT_PLAY); UNIMPLEMENTED ("iterate over the connections and allocate/establish each of them, creating and storing Feed objects"); } diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 72e89fa35..802823631 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -24,6 +24,7 @@ #include "proc/play/play-service.hpp" #include "proc/play/play-process.hpp" #include "lib/itertools.hpp" +#include "lib/util.hpp" #include @@ -54,10 +55,15 @@ namespace play { //using std::tr1::bind; using lib::Sync; using lib::RecursiveLock_NoWait; - using lib::filterIterator; + using lib::transformIterator; using std::tr1::weak_ptr; using std::tr1::bind; + using std::tr1::function; using std::tr1::placeholders::_1; + using util::remove_if; + using mobject::ModelPort; + + typedef proc::play::POutputManager POutputManager; namespace { // hidden local details of the service implementation.... @@ -74,7 +80,8 @@ namespace play { class ProcessTable : public Sync { - typedef std::vector > ProcTable; + typedef weak_ptr Entry; + typedef std::vector ProcTable; ProcTable processes_; @@ -85,6 +92,7 @@ namespace play { { lumiera::Play::Controller frontend; + Lock sync(this); frontend.activate (newProcess, bind (&ProcessTable::endProcess, this, _1 )); processes_.push_back (frontend); return frontend; @@ -95,7 +103,15 @@ namespace play { endProcess (PlayProcess* dyingProcess) { delete dyingProcess; - UNIMPLEMENTED ("process deregistration"); /// note the process might not be registered at all + + Lock sync(this); + remove_if (processes_, isDead); + } + + static bool + isDead (Entry const& e) + { + return e.expired(); } }; @@ -112,13 +128,39 @@ namespace play { * rendered data for output. Client code is assumed to access * this service through the lumiera::Play facade. */ - PlayService::PlayService() /////TODO Subsys::SigTerm terminationHandle); + PlayService::PlayService() : facadeAccess_(*this, "Player") , pTable_(new ProcessTable) { } + namespace { // details... + + OutputSlot& + resolveOutputConnection (ModelPort port, POutputManager outputResolver) + { + REQUIRE (outputResolver); + OutputSlot& slot = outputResolver->getOutputFor (port); + if (!slot.isFree()) + throw error::State("unable to acquire a suitable output slot" /////////////////////TICKET #197 #816 + , LUMIERA_ERROR_CANT_PLAY); + } + + /** try to establish an output slot for the given + * global bus or data production exit point. + * @param outputResolver a facility able to resolve to + * a concrete output slot within the actual context + * @throw error::State when resolution fails + */ + function + resolve (POutputManager outputResolver) + { + return bind (resolveOutputConnection, _1, outputResolver); + } + } + + /** * @note this is the core operation of the play and render service * @@ -133,12 +175,15 @@ namespace play { Play::Controller PlayService::connect(ModelPorts dataGenerators, Output outputDestinations) { - UNIMPLEMENTED ("build a PlayProcess"); - pTable_->establishProcess( - new PlayProcess (filterIterator (dataGenerators, - resolve(outputDestinations)))); + return pTable_->establishProcess( + new PlayProcess (transformIterator (dataGenerators, + resolve(outputDestinations)))); } + + + LUMIERA_ERROR_DEFINE (CANT_PLAY, "unable to build playback or render process for this configuration"); + }} // namespace proc::play diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index b0b39e4a1..9d70e23b6 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -58,6 +58,7 @@ namespace play { + /****************************************************** * Implementation access point: Player subsystem. * The PlayService is the primary way for clients to From 585adb88b6e53143386ca21cec32e94121e2591e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 25 Jun 2011 17:44:28 +0200 Subject: [PATCH 046/296] Refactoring: use the output Feed as intermediary construction step --- src/include/play-facade.h | 4 +- src/proc/play/output-slot.cpp | 12 +++- src/proc/play/play-controller.cpp | 10 ++++ src/proc/play/play-process.cpp | 91 ++++++++++++++++++++++++++++--- src/proc/play/play-process.hpp | 27 +++++---- src/proc/play/play-service.cpp | 38 ++----------- 6 files changed, 127 insertions(+), 55 deletions(-) diff --git a/src/include/play-facade.h b/src/include/play-facade.h index c1739bac6..e2b03713b 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -55,6 +55,8 @@ namespace play { namespace lumiera { namespace time = lib::time; + + using std::tr1::weak_ptr; /****************************************************************** @@ -113,7 +115,7 @@ namespace lumiera { uint getQuality() const; bool usesProxy() const; - operator std::tr1::weak_ptr(); + operator weak_ptr() const; }; diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp index ff2798ee0..ee04ed818 100644 --- a/src/proc/play/output-slot.cpp +++ b/src/proc/play/output-slot.cpp @@ -40,8 +40,16 @@ namespace play { - /** */ - + /** whether this output slot is occupied + * @return true if currently unconnected and + * able to connect and handle output data + */ + bool + OutputSlot::isFree() const + { + UNIMPLEMENTED ("connection state"); + } + diff --git a/src/proc/play/play-controller.cpp b/src/proc/play/play-controller.cpp index f8a3de185..5eb14b9f2 100644 --- a/src/proc/play/play-controller.cpp +++ b/src/proc/play/play-controller.cpp @@ -49,6 +49,7 @@ namespace lumiera { using lib::time::Time; using lib::time::Duration; using lib::time::TimeSpan; + using proc::play::PlayProcess; @@ -188,6 +189,15 @@ namespace lumiera { { UNIMPLEMENTED ("determine if the current render/playback uses proxy media"); } + + + /** @internal expose a weak reference to this handle object. + * Used by the ProcessTable to keep a link to all processes, + * without influencing their reference count */ + Play::Controller::operator weak_ptr() const + { + return weak_ptr (this->smPtr_); + } diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index 3859244a5..305ec6d82 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -22,34 +22,109 @@ #include "proc/play/play-process.hpp" +#include "lib/itertools.hpp" //#include //#include -//#include +#include //#include -namespace proc { - namespace play{ +namespace proc { +namespace play { // using std::string; // using lumiera::Subsys; // using std::auto_ptr; // using boost::scoped_ptr; // using std::tr1::bind; + + + namespace { // Implementation details... + + using std::tr1::bind; + using std::tr1::function; + using std::tr1::placeholders::_1; + using lib::transformIterator; + using lib::wrapIter; - namespace { // hidden local details of the service implementation.... + Feed + resolveOutputConnection (ModelPort port, POutputManager outputResolver) + { + REQUIRE (outputResolver); + OutputSlot& slot = outputResolver->getOutputFor (port); + if (!slot.isFree()) + throw error::State("unable to acquire a suitable output slot" /////////////////////TICKET #197 #816 + , LUMIERA_ERROR_CANT_PLAY); + return Feed (port,slot); + } - - - } // (End) hidden service impl details + + typedef function ConnectFunction; + + /** try to establish an output slot for the given + * global bus or data production exit point. + * @param outputResolver a facility able to resolve to + * a concrete output slot within the actual context + * @throw error::State when resolution fails + */ + ConnectFunction + resolve (POutputManager outputResolver) + { + return bind (resolveOutputConnection, _1, outputResolver); + } + Feed::Connections + transform (ModelPorts dataGenerators, ConnectFunction outputResolution) + { + return wrapIter( + transformIterator (dataGenerators, outputResolution)); + } - /** */ + } // (End) hidden service impl details + + + + + /** + * Factory: Initialise and configure a new PlayProcess. + * The caller gets to own and manage the returned process entry. + */ + PlayProcess* + PlayProcess::initiate (ModelPorts dataGenerators, POutputManager outputDestinations) + { + return new PlayProcess (transform (dataGenerators, + resolve(outputDestinations))); + + } + + + + /** @internal actually create and configure a play process instance */ + PlayProcess::PlayProcess (Feed::Connections pipeConnections) + { + if (isnil (pipeConnections)) + throw error::State ("creating a PlayProcess without any usable output connections" + , LUMIERA_ERROR_CANT_PLAY); + + UNIMPLEMENTED ("iterate over the connections and allocate/establish each of them, creating and storing Feed objects"); + while (pipeConnections) + { + + } + } + + + /** */ + Feed::Feed (ModelPort port, OutputSlot& output) + { + UNIMPLEMENTED("build an active playback/render feed"); + } + }} // namespace proc::play diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index aebd971dd..ae8b8d3a6 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -56,12 +56,15 @@ //#include "include/display-facade.h" //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" +#include "proc/mobject/model-port.hpp" #include "proc/play/output-manager.hpp" +#include "lib/iter-source.hpp" #include "lib/util.hpp" // #include //#include //#include +#include namespace proc { @@ -72,6 +75,10 @@ namespace play { // using lumiera::Display; // using lumiera::DummyPlayer; using util::isnil; + using mobject::ModelPort; + + typedef proc::play::POutputManager POutputManager; + typedef lib::IterSource::iterator ModelPorts; namespace error = lumiera::error; @@ -83,10 +90,14 @@ namespace play { * bound to operate on a single type of media data only. */ class Feed - : boost::noncopyable { + public: + typedef lib::IterSource::iterator Connections; + + Feed (ModelPort, OutputSlot&); }; + /****************************************************** * Playback/Render process within the Lumiera Player. @@ -107,17 +118,13 @@ namespace play { class PlayProcess : boost::noncopyable { + std::vector outputFeeds_; + + PlayProcess (Feed::Connections pipeConnections); public: - template - PlayProcess (CONS pipeConnections) - { - if (isnil (pipeConnections)) - throw error::State ("creating a PlayProcess without any usable output connections" - , LUMIERA_ERROR_CANT_PLAY); - - UNIMPLEMENTED ("iterate over the connections and allocate/establish each of them, creating and storing Feed objects"); - } + static PlayProcess* + initiate (ModelPorts dataGenerators, POutputManager outputDestinations); }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 802823631..aab3306b7 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -23,13 +23,12 @@ #include "proc/play/play-service.hpp" #include "proc/play/play-process.hpp" -#include "lib/itertools.hpp" #include "lib/util.hpp" #include //#include -#include +//#include #include //#include @@ -55,15 +54,11 @@ namespace play { //using std::tr1::bind; using lib::Sync; using lib::RecursiveLock_NoWait; - using lib::transformIterator; using std::tr1::weak_ptr; using std::tr1::bind; - using std::tr1::function; +//using std::tr1::function; using std::tr1::placeholders::_1; using util::remove_if; - using mobject::ModelPort; - - typedef proc::play::POutputManager POutputManager; namespace { // hidden local details of the service implementation.... @@ -135,30 +130,6 @@ namespace play { - namespace { // details... - - OutputSlot& - resolveOutputConnection (ModelPort port, POutputManager outputResolver) - { - REQUIRE (outputResolver); - OutputSlot& slot = outputResolver->getOutputFor (port); - if (!slot.isFree()) - throw error::State("unable to acquire a suitable output slot" /////////////////////TICKET #197 #816 - , LUMIERA_ERROR_CANT_PLAY); - } - - /** try to establish an output slot for the given - * global bus or data production exit point. - * @param outputResolver a facility able to resolve to - * a concrete output slot within the actual context - * @throw error::State when resolution fails - */ - function - resolve (POutputManager outputResolver) - { - return bind (resolveOutputConnection, _1, outputResolver); - } - } /** @@ -173,11 +144,10 @@ namespace play { * calculated media data to the outputs. */ Play::Controller - PlayService::connect(ModelPorts dataGenerators, Output outputDestinations) + PlayService::connect (ModelPorts dataGenerators, Output outputDestinations) { return pTable_->establishProcess( - new PlayProcess (transformIterator (dataGenerators, - resolve(outputDestinations)))); + PlayProcess::initiate(dataGenerators, outputDestinations)); } From f46cc268511e0d60e31d3faf96ae640702a29244 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 26 Jun 2011 03:59:03 +0200 Subject: [PATCH 047/296] factor out convenience shortcut transform iterator and wrap result into IterSource --- src/lib/iter-source.hpp | 31 +++++++++++++++++++++++++++++++ src/proc/play/play-process.cpp | 12 +----------- tests/40components.tests | 1 + tests/lib/iter-source-test.cpp | 24 +++++++++++++++++++++++- 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/lib/iter-source.hpp b/src/lib/iter-source.hpp index 7910ecd5c..83f55e373 100644 --- a/src/lib/iter-source.hpp +++ b/src/lib/iter-source.hpp @@ -279,6 +279,14 @@ namespace lib { typedef typename IterSource::iterator Iter; }; + template + struct _TransformIterT + { + typedef typename _ProducedOutput::Type ResVal; + typedef TransformIter TransIter; + typedef typename IterSource::iterator Iter; + }; + template struct _PairIterT { @@ -329,6 +337,28 @@ namespace lib { } + /** pipes a given Lumiera Forward Iterator through + * a transformation function and wraps the resulting + * transforming Iterator, exposing just a IterSource. + * This convenience shortcut can be used to build a + * processing chain; the resulting IterSource will + * hide any involved detail types. + * @note as with any IterSource, there is one virtual + * function call for every fetched element. + */ + template + typename _TransformIterT::Iter + transform (IT const& source, FUN processingFunc) + { + typedef typename _TransformIterT::ResVal ValType; + typedef typename _TransformIterT::TransIter TransIT; + + return IterSource::build ( + new WrappedLumieraIterator ( + transformIterator (source, processingFunc))); + } + + /** @return a Lumiera Forward Iterator to yield * all the keys of the given Map or Hashtable */ @@ -424,6 +454,7 @@ namespace lib { } using iter_source::wrapIter; + using iter_source::transform; using iter_source::eachMapKey; using iter_source::eachDistinctKey; using iter_source::eachValForKey; diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index 305ec6d82..ca1417659 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -46,8 +46,7 @@ namespace play { using std::tr1::bind; using std::tr1::function; using std::tr1::placeholders::_1; - using lib::transformIterator; - using lib::wrapIter; + using lib::transform; Feed @@ -76,15 +75,6 @@ namespace play { return bind (resolveOutputConnection, _1, outputResolver); } - - - Feed::Connections - transform (ModelPorts dataGenerators, ConnectFunction outputResolution) - { - return wrapIter( - transformIterator (dataGenerators, outputResolution)); - } - } // (End) hidden service impl details diff --git a/tests/40components.tests b/tests/40components.tests index 999817faa..a81b77d3b 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -470,6 +470,7 @@ END TEST "Iterable data source" IterSource_test 13 < (arg[0]); verify_simpleIters(); + verify_transformIter(); verify_MapWrappers(); verify_MapWrappers(); @@ -203,6 +204,28 @@ namespace test{ } + void + verify_transformIter() + { + WrappedList customList(NUM_ELMS); + WrappedList::iterator sourceValues = customList.begin(); + + TimeIter tIt (transform (sourceValues, makeTime)); + CHECK (!isnil (tIt)); + pullOut (tIt); + CHECK (!tIt); + } + + /** transformation function, to be applied for each element: + * just build a time value, using the input as 1/4 seconds + */ + static TimeVar + makeTime (int input_sec) + { + return time::Time (time::FSecs (input_sec, 4)); + } + + template void @@ -280,7 +303,6 @@ namespace test{ CHECK (vals); pullOut (vals); // should produce anything between 1 and 100 entries CHECK (!vals); - } }; From 08df994fb1ad73c4ca6bab51c8b8a9ff21a2d22d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 26 Jun 2011 04:02:39 +0200 Subject: [PATCH 048/296] fix unit test broken by 7dad280 (14.6.2011) --- tests/lib/subsystem-runner-test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/lib/subsystem-runner-test.cpp b/tests/lib/subsystem-runner-test.cpp index 6db7ebf99..f15fe9fe2 100644 --- a/tests/lib/subsystem-runner-test.cpp +++ b/tests/lib/subsystem-runner-test.cpp @@ -76,6 +76,7 @@ namespace test { LUMIERA_ERROR_DEFINE( TEST, "simulated failure."); using error::LUMIERA_ERROR_LOGIC; + using error::LUMIERA_ERROR_STATE; @@ -400,7 +401,7 @@ namespace test { unit3.depends (unit2); SubsystemRunner runner(dummyOpt); - VERIFY_ERROR (LOGIC, runner.maybeRun (unit4) ); // failure to bring up prerequisites is detected + VERIFY_ERROR (STATE, runner.maybeRun (unit4) ); // failure to bring up prerequisites is detected CHECK ( unit1.isRunning()); CHECK ( unit2.isRunning()); CHECK (!unit3.isRunning()); From ea8841b7ad6c2da7084a46cc797f9bc610fce63f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 27 Jun 2011 03:09:38 +0200 Subject: [PATCH 049/296] analysis of a design problem and decision for the OutputSlot --- wiki/renderengine.html | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index ce2cd3f38..00dee8255 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3313,7 +3313,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3344,10 +3344,20 @@ The concrete OutputSlot implementation is owned and managed by the facility actu
 -----
 !Implementation / design problems
 How to handle the selection of those two data exchange models!
--- Problem is, typically this choice isn't up to the client; rather, the concrete OutputSlot implementation dictates what model to use. Yet I wanted to handle these through //generic programming// -- because they can't be subsumed under a single interface without distorting the usage or the structure.
+-- Problem is, typically this choice isn't up to the client; rather, the concrete OutputSlot implementation dictates what model to use. But, as it stands, the client needs to cooperate and behave differently to a certain degree. Unless we manage to factor out an common denominator of both models.
 
-Thus: Client gets an {{{OutputSlot&}}}, without knowing the exact implementation type &rArr; how can the client pick up the right strategy, note, at //compile time!//
+Thus: Client gets an {{{OutputSlot&}}}, without knowing the exact implementation type &rArr; how can the client pick up the right strategy?
+Solving this problem through //generic programming// -- i.e coding both cases effectively different, but similar -- would require to provide both implementation options already at //compile time!//
 
+{{red{currently}}} I see two possible, yet quite different approaches...
+;generic
+:when creating individual jobs, we utilise a //factory optained from the output slot.//
+;unified
+:extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
+!!!!discussion
+the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling.
+If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calulation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
+&rArr; conclusion: go for the unified approach!
 
From 650e73c45455b765307e2f1329a588cf25b25487 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 5 Jul 2011 00:56:57 +0200 Subject: [PATCH 050/296] OutputSlot: draft buffer handover protocol, remove the diferent models --- src/proc/play/output-slot.hpp | 17 +++++++---------- wiki/renderengine.html | 14 ++++++++++---- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 36f6de9b7..65fbb1c86 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -65,16 +65,17 @@ namespace play { /** established output channel */ class Connection; - class BufferHandoverSink - : public lib::Handle + class BufferProvider { public: - void emit(Time, BuffHandle); + ~BufferProvider() { } + + BuffHandle lockBufferFor(Time); }; - class SharedBufferSink + class DataSink : public lib::Handle { @@ -96,14 +97,10 @@ namespace play { public: virtual ~OutputSlot(); - typedef lib::IterSource::iterator Opened_BufferHandoverSinks; - typedef lib::IterSource::iterator Opened_SharedBufferSinks; + typedef lib::IterSource::iterator OpenedSinks; - template struct Allocation { - typedef typename lib::IterSource::iterator OpenedSinks; - OpenedSinks getOpenedSinks(); bool isActive(); @@ -114,7 +111,7 @@ namespace play { bool isFree() const; - Allocation + Allocation allocate(); private: diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 00dee8255..e218ca445 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3313,7 +3313,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3322,10 +3322,10 @@ Each OutputSlot is an unique and distinguishable entity. It corresponds explicit
 
 In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
 
-Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The size and other characteristics of the data frames is assumed to be suitable. Typically this won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
+Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The calculating process feeds its results into those handles. Size and other characteristics of the data frames is assumed to be suitable, which typically won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
 
 !!!data exchange models
-Data is handed over by the client invoking an {{{emit(time,...)}}} function on the sink handle. There are two different models how this data hand-over might be performed. On allocation of a slot, the client has to commit to one of them, allowing the output slot to adapt accordingly
+Data is handed over by the client invoking an {{{emit(time,...)}}} function on the sink handle. Theoretically there are two different models how this data hand-over might be performed. This corresponds to the fact, that in some cases our own code manages the output and the buffers, while in other situations we intend to use existing library solutions or even external server applications to handle output
 ;buffer handover model
 :the client owns the data buffer and cares for allocation and de-allocation. The {{{emit()}}}-call just propagates a pointer to the buffer holding the data ready for output. The output slot implementation in turn has the liability to copy or otherwise use this data within a given time limit.
 ;shared buffer model
@@ -3354,10 +3354,16 @@ Solving this problem through //generic programming// -- i.e coding both cases ef
 :when creating individual jobs, we utilise a //factory optained from the output slot.//
 ;unified
 :extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
-!!!!discussion
+!!!discussion
 the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling.
 If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calulation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
 &rArr; conclusion: go for the unified approach!
+
+!!!unified data exchange cycle
+The nominal time of a frame to be delivered is used as an ID throughout that cycle
+# within a defined time window prior to delivery, the client can retrieve the buffer from the ''buffer provider''.
+# the client has to ''emit'' within a (short) time window pior to deadline
+# now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.
 
From 7e3054df18384cd7c531929fc221d638ef652a43 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 8 Jul 2011 03:54:26 +0200 Subject: [PATCH 051/296] considering timing glitches in detail --- wiki/renderengine.html | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index e218ca445..7d50e9aa7 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3313,7 +3313,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3334,12 +3334,13 @@ Data is handed over by the client invoking an {{{emit(time,...)}}} function on t
 !!!timing expectations
 Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These timings are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, &raquo;latency&laquo; means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
 
-The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is a job running within the engine scheduler and thus can be configured to run within certain limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
+The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is a job running within the engine scheduler and thus can be configured to run //after// another job has finished, and to run within certain time limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
 
 {{red{TODO 6/11}}}in this spec, both data exchange models exhibit a weakness regarding the releasing of buffers. At which time is it safe to release a buffer, when the handover didn't happen? Do we need an explicit callback, and how could this callback be triggered? This is similar to the problem of closing a network connection, i.e. the problem is generally unsolvable, but can be handled pragmatically within certain limits.
 
 !!!Lifecycle and storage
 The concrete OutputSlot implementation is owned and managed by the facility actually providing this output possibility. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. This implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be deregistered explicitly prior to destruction -- and this deregistration may block until all clients using this slot are terminated. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferrably just emitting a signal (callback functor).
+{{red{TODO 7/11: Deregistration is an unsolved problem....}}}
 
 -----
 !Implementation / design problems
@@ -3351,20 +3352,38 @@ Solving this problem through //generic programming// -- i.e coding both cases ef
 
 {{red{currently}}} I see two possible, yet quite different approaches...
 ;generic
-:when creating individual jobs, we utilise a //factory optained from the output slot.//
+:when creating individual jobs, we utilise a //factory obtained from the output slot.//
 ;unified
 :extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
 !!!discussion
 the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling.
-If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calulation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
+If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calculation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
 &rArr; conclusion: go for the unified approach!
 
 !!!unified data exchange cycle
 The nominal time of a frame to be delivered is used as an ID throughout that cycle
 # within a defined time window prior to delivery, the client can retrieve the buffer from the ''buffer provider''.
-# the client has to ''emit'' within a (short) time window pior to deadline
+# the client has to ''emit'' within a (short) time window prior to deadline
 # now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.
-
+ +!!!lapses +This data exchange protocol operates on a rather low level; there is only limited protection against timing glitches +| !step|!problem ||!consequences | !protection | +| (1)|out of time window ||interference with previous/later use of the buffer | prevent in scheduler! | +|~|does not happen ||harmless as such | emit ignored | +|~|buffer unavailable ||inhibits further operation | ↯ | +| (2)|out of time window ||harmless as such | emit ignored | +|~|out of order ||allowed, unless out of time | -- | +|~|does not happen ||frame treated as glitch | -- | +|~|buffer unallocated ||frame treated as glitch | emit ignored | +| (3)|emit missing ||frame treated as glitch | -- | +|~|fail to release buffer ||unable to use buffer further | mark unavailable | +|~|buffer unavailable ||serious malfunction of playback | request playback stop | + +Thus there are two serious problem situations +* allocating the buffer out of time window bears the danger of output data corruption; but the general assumption is for the scheduler to ensure each job start time remains within the defined window and all prerequisite jobs have terminated successfully. To handle clean-up, we additionally need special jobs running always, in order, and be notified of prerequisite failures. +* failure to release a buffer timely blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole. +&rarr; SchedulerRequirements
The Lumiera Processing Layer is comprised of various subsystems and can be separated into a low-level and a high-level part. At the low-level end is the [[Render Engine|OverviewRenderEngine]] which basically is a network of render nodes cooperating closely with the Backend Layer in order to carry out the actual playback and media transforming calculations. Whereas on the high-level side we find several different [[Media Objects|MObjects]] that can be placed into the session, edited and manipulated. This is complemented by the [[Asset Management|Asset]], which is the "bookkeeping view" of all the different "things" within each [[Session|SessionOverview]].
@@ -4999,6 +5018,17 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin
 
 &rarr; QuantiserImpl
+
+
The Scheduler is responsible for geting the individual render jobs to run. The basic idea is that individual render jobs //should never block// -- and thus the calculation of a single frame might be split into several jobs, including resource fetching. This, together with the data exchange protocol defined for the OutputSlot, and the requirements of storage management (especially releasing of superseded render nodes), leads to certain requirements to be ensured by the scheduler:
+;ordering of jobs
+:the scheduler has to ensure all prerequisites of a given job are met
+;job time window
+:when it's not possible to run a job within the defined target time window, it must be marked as failure
+;failure propagation
+:when a job fails, either due to an job internal error, or by timing glitch, any dependent jobs need to receive that failure state
+;guaranteed execution
+:some jobs are marked as "ensure run". These need to run reliable, even when prerequisite jobs fail -- and this failure state needs to be propagated
+
A link to relate a compound of [[nested placement scopes|PlacementScope]] to the //current// session and the //current//&nbsp; [[focus for querying|QueryFocus]] and exploring the structure. ScopeLocator is a singleton service, allowing to ''explore'' a [[Placement]] as a scope, i.e. discover any other placements within this scope, and allowing to locate the position of this scope by navigating up the ScopePath finally to reach the root scope of the HighLevelModel.
 

From a199bff92b89cee380ec7688062865a62fa14895 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 9 Jul 2011 02:17:37 +0200
Subject: [PATCH 052/296] BufferProvider as a frontend to buffer management

---
 .../{buffhandle.cpp => buffer-provider.cpp}   |  7 +-
 src/proc/engine/buffer-provider.hpp           | 72 +++++++++++++++++++
 src/proc/engine/buffhandle.hpp                | 21 +++---
 src/proc/play/output-slot.hpp                 |  2 +-
 wiki/renderengine.html                        | 27 ++++---
 5 files changed, 110 insertions(+), 19 deletions(-)
 rename src/proc/engine/{buffhandle.cpp => buffer-provider.cpp} (84%)
 create mode 100644 src/proc/engine/buffer-provider.hpp

diff --git a/src/proc/engine/buffhandle.cpp b/src/proc/engine/buffer-provider.cpp
similarity index 84%
rename from src/proc/engine/buffhandle.cpp
rename to src/proc/engine/buffer-provider.cpp
index 24573990e..e58168c9a 100644
--- a/src/proc/engine/buffhandle.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -1,5 +1,5 @@
 /*
-  BuffHandle  -  Buffer handling support for the render engine
+  BufferProvider  -  Abstraction for Buffer management during playback/render
 
   Copyright (C)         Lumiera.org
     2008,               Hermann Vosseler 
@@ -21,11 +21,14 @@
 * *****************************************************/
 
 
-#include "proc/engine/buffhandle.hpp"
+#include "proc/engine/buffer-provider.hpp"
 
 namespace engine {
 
 
+  BufferProvider::~BufferProvider() { }
+  
+  
   /**  */
 
 
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
new file mode 100644
index 000000000..981bfe799
--- /dev/null
+++ b/src/proc/engine/buffer-provider.hpp
@@ -0,0 +1,72 @@
+/*
+  BUFFER-PROVIDER.hpp  -  Abstraction for Buffer management during playback/render
+
+  Copyright (C)         Lumiera.org
+    2011,               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 buffer-provider.hpp
+ ** Abstraction to represent buffer management and lifecycle within the render engine.
+ ** It turns out that --  throughout the render engine implementation -- we never need
+ ** direct access to the buffers holding media data. Buffers are just some entity to be \em managed,
+ ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementatino detail.
+ ** The code within the render engine just pushes around BufferHandle objects, which act as a front-end,
+ ** being created by and linked to a BufferProvider implementation. There is no need to manage the lifecycle
+ ** of buffers automatically, because the use of buffers is embedded into the render calculation cycle,
+ ** which follows a rather strict protocol anyway. Relying on the capabilities of the scheduler,
+ ** the sequence of individual jobs in the engine ensures...
+ ** - that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
+ ** - that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
+ ** - that buffers are marked as free ("released") after doing the actual calculations.
+ ** 
+ ** @see state.hpp
+ ** @see output-slot.hpp
+ */
+
+#ifndef PROC_ENGINE_BUFFR_PROVIDER_H
+#define PROC_ENGINE_BUFFR_PROVIDER_H
+
+
+#include "lib/error.hpp"
+#include "proc/engine/buffhandle.hpp"
+
+#include 
+
+
+namespace engine {
+  
+  
+  
+  /**
+   * Handle for a buffer for processing data, abstracting away the actual implementation.
+   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
+   * 
+   * @todo as of 6/2011 buffer management within the engine is still a bit vague
+   */
+  class BufferProvider
+    : boost::noncopyable
+    {
+      
+    public:
+      virtual ~BufferProvider();  ///< this is an interface
+    };
+  
+  
+  
+} // namespace engine
+#endif
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 0f6378396..3d7bcbfac 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -24,12 +24,17 @@
  ** Various bits needed to support the buffer management within the render nodes.
  ** When pulling data from predecessor nodes and calculating new data, each render node
  ** needs several input and output buffers. These may be allocated and provided by several
- ** different "buffer providers" (for example the frame cache). For accessing those buffers,
- ** the node needs to keep a table of buffer pointers, and for releasing the buffers later
- ** on, we need some handles. The usage pattern of those buffer pointer tables is stack-like,
- ** thus it makes sense to utilise a single large buffer pointer array per pull() calldown
- ** sequence and dynamically claim small chunks for each node.
- **
+ ** different "buffer providers" (for example the frame cache). Typically, the real buffers
+ ** will be passed as parameters to the actual job instance when scheduled, drawing on the
+ ** results of prerequisite jobs. Yet the actual job implementation remains agnostic with
+ ** respect to the way actual buffers are provided; the invocation just pushes BuffHandle
+ ** objects around. The actual render function gets an array of C-pointers to the actual
+ ** buffers, and for accessing those buffers, the node needs to keep a table of buffer
+ ** pointers, and for releasing the buffers later on, we utilise the buffer handles.
+ ** The usage pattern of those buffer pointer tables is stack-like, thus the actual
+ ** implementation utilises a single large buffer pointer array per pull() call
+ ** sequence and dynamically claims small chunks for each node.
+ ** 
  ** @see nodewiring-def.hpp
  ** @see nodeoperation.hpp
  ** @see bufftable.hpp       storage for the buffer table
@@ -105,11 +110,11 @@ namespace engine {
   /**
    * Buffer Type information.
    * Given a BufferDescriptor, it is possible to allocate a buffer
-   * of suitable size and type by using State::allocateBuffer().
+   * of suitable size and type by using BufferProvider::allocateBuffer().
    */
   struct BufferDescriptor
     {
-      lumiera::StreamType& sType_;
+      long typeToken_; 
     };
   
   
diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp
index 65fbb1c86..b5d0c3d45 100644
--- a/src/proc/play/output-slot.hpp
+++ b/src/proc/play/output-slot.hpp
@@ -39,7 +39,7 @@
 #include "lib/error.hpp"
 #include "lib/handle.hpp"
 #include "lib/time/timevalue.hpp"
-#include "proc/engine/buffhandle.hpp"
+#include "proc/engine/buffer-provider.hpp"
 #include "lib/iter-source.hpp"
 //#include "lib/sync.hpp"
 
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 7d50e9aa7..1826d59ef 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1077,6 +1077,17 @@ Please note the shortcomings and logical contradictions in the solution currentl
 * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''buffer handles'', which act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
+* that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
+* that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
+* that buffers are marked as free ("released") after doing the actual calculations.
+
+__see also__
+&rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
+&rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
+
+
//Building the fixture is actually at the core of the [[builder's operation|Builder]]//
 {{red{WIP as of 11/10}}} &rarr; see also the [[planning page|PlanningBuildFixture]]
@@ -3313,7 +3324,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3357,12 +3368,12 @@ Solving this problem through //generic programming// -- i.e coding both cases ef
 :extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
 !!!discussion
 the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling.
-If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calculation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
+If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calculation loops for frame processing anyway. When following this approach, the BufferProvider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
 &rArr; conclusion: go for the unified approach!
 
 !!!unified data exchange cycle
 The nominal time of a frame to be delivered is used as an ID throughout that cycle
-# within a defined time window prior to delivery, the client can retrieve the buffer from the ''buffer provider''.
+# within a defined time window prior to delivery, the client can ''allocate and retrieve the buffer'' from the BufferProvider.
 # the client has to ''emit'' within a (short) time window prior to deadline
 # now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.
 
@@ -4893,7 +4904,7 @@ At first sight the link between asset and clip-MO is a simple logical relation b
 [img[Entities comprising the Render Engine|uml/fig128389.png]]
 
-
+
Below are some notes regarding details of the actual implementation of the render process and processing node operation. In the description of the [[render node operation protocol|NodeOperationProtocol]] and the [[mechanics of the render process|RenderMechanics]], these details were left out deliberately.
 
 !Layered structure of State
@@ -4923,7 +4934,7 @@ We //do employ//&nbsp; some virtual calls for the buffer management in order
 * only output buffers are allocated. It is //never necessary//&nbsp; to allocate input buffers!
 * buffers are to be allocated as late as possible, typically just before invoking {{{process()}}}
 * buffers are allways allocated by calling to the preceeding StateAdapter in the callstack ("parent stae"), because of the possibility of writing the result to cache.
-* {{{pull()}}} returns a handle for the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StaeProxy.
+* {{{pull()}}} returns a handle for the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StateProxy.
 * any other buffers filled with results in the course of the same {{{process()}}} call can be released immediately before returning from the {{{pull()}}}
 * similar, and input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
 * buffers are allways released by calling to the "current state" (which is a StateProxy), providing the buffer-ID to be released
@@ -5497,11 +5508,11 @@ if (oldText.indexOf("SplashScreen")==-1)
 
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
-
+
An Object representing a //Render Process// and containing associated state information.
-* it is created in the Controller subsystem while initiating the BuildProcess
+* it is created in the Player subsystem while initiating the RenderProcess
 * it is passed on to the generated Render Engine, which in turn passes it down to the individual Processors
-* moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of Thread communication if necessary. (thus the name Proxy)
+* moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of state synchronisation and management if necessary. (thus the name Proxy)
 * in a future version, it may also encapsulate the communication in a distributed render farm
 
From 7adb8149db3036b752e3ffb0874aeda2f324e3f9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 9 Jul 2011 02:44:24 +0200 Subject: [PATCH 053/296] back to test-driven brainstorming again --- src/proc/engine/buffer-provider.hpp | 6 ++ src/proc/engine/buffhandle.hpp | 2 +- src/proc/play/diagnostic-output-slot.hpp | 73 +++++++++++++++++++ src/proc/play/output-slot.hpp | 11 +-- .../proc/play/output-slot-protocol-test.cpp | 66 +++++++++++++++++ 5 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 src/proc/play/diagnostic-output-slot.hpp create mode 100644 tests/components/proc/play/output-slot-protocol-test.cpp diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 981bfe799..907cb9806 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -64,6 +64,12 @@ namespace engine { public: virtual ~BufferProvider(); ///< this is an interface + + ///////TODO: is there any generic way to obtain a BufferDescriptor; then we should expose it here... + + virtual BuffHandle lockBufferFor (BufferDescriptor const&) =0; + virtual void releaseBuffer (BuffHandle const&) =0; ////////TODO not quite sure what information to pass here + }; diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 3d7bcbfac..d7c5ecd87 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -110,7 +110,7 @@ namespace engine { /** * Buffer Type information. * Given a BufferDescriptor, it is possible to allocate a buffer - * of suitable size and type by using BufferProvider::allocateBuffer(). + * of suitable size and type by using BufferProvider::lockBuffer(). */ struct BufferDescriptor { diff --git a/src/proc/play/diagnostic-output-slot.hpp b/src/proc/play/diagnostic-output-slot.hpp new file mode 100644 index 000000000..e0e4a7f0a --- /dev/null +++ b/src/proc/play/diagnostic-output-slot.hpp @@ -0,0 +1,73 @@ +/* + DIAGNOSTIC-OUTPUT-SLOT.hpp - helper for testing against the OutputSlot interface + + Copyright (C) Lumiera.org + 2011, 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 diagnostic-output-slot.hpp + ** An facility for writing unit-tests against the OutputSlot interface. + ** + ** @see output-slot-protocol-test.cpp + */ + + +#ifndef PROC_PLAY_DIAGNOSTIC_OUTPUT_SLOT_H +#define PROC_PLAY_DIAGNOSTIC_OUTPUT_SLOT_H + + +#include "lib/error.hpp" +#include "proc/play/output-slot.hpp" +//#include "lib/sync.hpp" + +//#include +//#include +//#include +//#include +//#include + + +namespace proc { +namespace play { + +//using std::string; + +//using std::vector; +//using std::tr1::shared_ptr; +//using boost::scoped_ptr; + + + + /******************************************************************** + * Helper for unit tests: Mock output sink. + * + * @todo write type comment + */ + class DiagnosticOutputSlot + : public OutputSlot + { + public: + + private: + + }; + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index b5d0c3d45..d784bad85 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -28,7 +28,7 @@ ** possibilities can be added and removed dynamically from various components (backend, GUI), ** all using the same resolution and mapping mechanisms ** - ** @see output-test-slot.hpp ////TODO + ** @see diagnostic-output-slot.hpp ////TODO */ @@ -54,6 +54,7 @@ namespace proc { namespace play { using ::engine::BuffHandle; + using ::engine::BufferProvider; using lib::time::Time; //using std::string; @@ -65,14 +66,6 @@ namespace play { /** established output channel */ class Connection; - class BufferProvider - { - - public: - ~BufferProvider() { } - - BuffHandle lockBufferFor(Time); - }; class DataSink diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp new file mode 100644 index 000000000..2bdc7b09d --- /dev/null +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -0,0 +1,66 @@ +/* + OutputSlotProtocol(Test) - covering the basic usage cycle of an output slot + + Copyright (C) Lumiera.org + 2011, 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 "lib/test/run.hpp" +//#include "lib/util.hpp" +#include "proc/play/diagnostic-output-slot.hpp" + +//#include +//#include + +//using boost::format; +//using std::string; +//using std::cout; + + +namespace engine{ +namespace test { + +// using lib::AllocationCluster; +// using mobject::session::PEffect; + + + namespace { // Test fixture + + } + + + /******************************************************************* + * @test basic render node properties and behaviour. + */ + class OutputSlotProtocol_test : public Test + { + virtual void + run (Arg) + { + UNIMPLEMENTED ("build a mock output slot and perform a full lifecycle"); + } + }; + + + /** Register this test class... */ + LAUNCHER (OutputSlotProtocol_test, "unit player"); + + + +}} // namespace engine::test From 691ab4b8b5b8c725702749108dfb52ce270155ab Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 10 Jul 2011 18:18:20 +0200 Subject: [PATCH 054/296] WIP draft simple OutputSlot usage --- .../proc/play/output-slot-protocol-test.cpp | 89 ++++++++++++++++++- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index 2bdc7b09d..c83d6bff3 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -24,6 +24,7 @@ #include "lib/test/run.hpp" //#include "lib/util.hpp" #include "proc/play/diagnostic-output-slot.hpp" +#include "proc/engine/buffhandle.hpp" //#include //#include @@ -33,11 +34,13 @@ //using std::cout; -namespace engine{ +namespace proc { +namespace play { namespace test { // using lib::AllocationCluster; // using mobject::session::PEffect; + using ::engine::BuffHandle; namespace { // Test fixture @@ -46,7 +49,11 @@ namespace test { /******************************************************************* - * @test basic render node properties and behaviour. + * @test verify the OutputSlot interface and base implementation + * by performing full data exchange cycle. This is a + * kind of "dry run" for documentation purposes, + * both the actual OutputSlot implementation + * as the client using this slot are Mocks. */ class OutputSlotProtocol_test : public Test { @@ -54,7 +61,81 @@ namespace test { run (Arg) { UNIMPLEMENTED ("build a mock output slot and perform a full lifecycle"); - } + verifyStandardCase(); + } + + + void + verifyStandardCase() + { + // Create Test fixture. + // In real usage, the OutputSlot will be preconfigured + // (Media format, number of channels, physical connections) + // and then registered with / retrieved from an OutputManager + OutputSlot& oSlot = DiagnosticOutputSlot::build(); + + // Client claims the OutputSlot + // and opens it for exclusive use. + Allocation alloc = oSlot.allocate(); + + // Now the client is able to prepare + // "calculation streams" for the individual + // Channels to be output through this slot. + OutputSlot::OpenedSinks sinks = alloc.getOpenedSinks(); + DataSink sink1 = *sinks++; + DataSink sink2 = *sinks++; + + // within the frame-calculation "loop" + // we perform an data exchange cycle + int64_t frameNr = 123; + BuffHandle buff00 = sink1.lockBufferFor (frameNr); + BuffHandle buff10 = sink2.lockBufferFor (frameNr); + buff00.create(); + buff10.create(); + + // rendering process calculates content.... + buff00.access() = testData[0,0]; + + // while further frames might be processed in parallel + BuffHandle buff11 = sink2.lockBufferFor (++frameNr); + buff11.create(); + buff11.access() = testData[1,1]; + buff10.access() = testData[1,0]; + + // Now it's time to emit the output + sink2.emit (frameNr-1); + sink2.emit (frameNr ); + sink1.emit (frameNr-1); + // that's all for the client + + // Verify sane operation.... + DiagnosticOutputSlot checker = DiagnosticOutputSlot::access(oSlot); + CHECK (checker.buffer_was_used (0,0)); + CHECK (checker.buffer_unused (0,1)); + CHECK (checker.buffer_was_used (1,0)); + CHECK (checker.buffer_was_used (1,1)); + + CHECK (checker.buffer_was_closed (0,0)); + CHECK (checker.buffer_was_closed (1,0)); + CHECK (checker.buffer_was_closed (1,1)); + + CHECK ( checker.emitted (0,0)); + CHECK (!checker.emitted (0,1)); + CHECK ( checker.emitted (1,0)); + CHECK ( checker.emitted (1,1)); + + DiagnosticOutputSlot::OutFrames stream0 = checker.getChannel(0); + DiagnosticOutputSlot::OutFrames stream1 = checker.getChannel(1); + + CHECK ( stream0); + CHECK (*stream0++ == testData[0,0]); + CHECK (!stream0); + + CHECK ( stream1); + CHECK (*stream1++ == testData[1,0]); + CHECK (*stream1++ == testData[1,1]); + CHECK (!stream1); + } }; @@ -63,4 +144,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::play::test From 9f7a46110b7ce0ce3f55d1ee29da1add4bb00428 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 11 Jul 2011 00:12:18 +0200 Subject: [PATCH 055/296] fix labels in UML --- doc/devel/uml/fig144005.png | Bin 15144 -> 15263 bytes uml/lumiera/133637 | 16 ++++++++++++---- uml/lumiera/144005.diagram | 22 +++++++++++----------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/doc/devel/uml/fig144005.png b/doc/devel/uml/fig144005.png index 93627a5ac9bd816faa12a234164a0fdbff947f54..6ca73fc491d307d8571546033fe88bf9535039c6 100644 GIT binary patch literal 15263 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 diff --git a/uml/lumiera/133637 b/uml/lumiera/133637 index d788a91a9..6336ed674 100644 --- a/uml/lumiera/133637 +++ b/uml/lumiera/133637 @@ -1,6 +1,6 @@ format 58 "Play" // ProcessingLayer::Play - revision 1 + revision 2 modified_by 5 "hiv" // class settings //class diagram settings @@ -273,7 +273,7 @@ ${inlines} end end - classinstance 146181 "video" + classinstance 146181 "video-L" type class_ref 176901 // CalcStream attributes end @@ -281,7 +281,7 @@ ${inlines} end end - classinstance 146309 "video" + classinstance 146309 "video-R" type class_ref 176901 // CalcStream attributes end @@ -305,7 +305,15 @@ ${inlines} end end - classinstance 146693 "sound-Z" + classinstance 146693 "sound-Y" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 148357 "sound-Z" type class_ref 176901 // CalcStream attributes end diff --git a/uml/lumiera/144005.diagram b/uml/lumiera/144005.diagram index 42a499609..8a28bdf33 100644 --- a/uml/lumiera/144005.diagram +++ b/uml/lumiera/144005.diagram @@ -12,10 +12,10 @@ end classinstancecanvas 128389 classinstance_ref 146053 // xyz 171 240 2000 end -classinstancecanvas 128517 classinstance_ref 146181 // video +classinstancecanvas 128517 classinstance_ref 146181 // video-L xyz 233 194 2000 color lightgreen end -classinstancecanvas 128645 classinstance_ref 146309 // video +classinstancecanvas 128645 classinstance_ref 146309 // video-R xyz 233 216 2000 color lightgreen end classinstancecanvas 128773 classinstance_ref 146437 // sound-W @@ -24,12 +24,9 @@ end classinstancecanvas 128901 classinstance_ref 146565 // sound-X xyz 233 291 2000 color lightgreen end -classinstancecanvas 129029 classinstance_ref 146693 // sound-Z +classinstancecanvas 129029 classinstance_ref 146693 // sound-Y xyz 233 313 2000 color lightgreen end -classinstancecanvas 129157 classinstance_ref 146693 // sound-Z - xyz 233 335 2000 color lightgreen -end classinstancecanvas 132101 classinstance_ref 146821 // xyz 420 269 2000 color lightblue end @@ -46,6 +43,9 @@ end classinstancecanvas 132741 classinstance_ref 147205 // xyz 233 46 2000 end +classinstancecanvas 132869 classinstance_ref 148357 // sound-Z + xyz 233 335 2000 color lightgreen +end objectlinkcanvas 129285 norel geometry VH from ref 128133 z 1999 to point 139 181 @@ -81,14 +81,14 @@ objectlinkcanvas 130949 norel from ref 128389 z 1999 to point 194 321 line 131077 z 1999 to ref 129029 no_role_a no_role_b -objectlinkcanvas 131205 norel - geometry VH - from ref 128389 z 1999 to point 194 343 - line 131333 z 1999 to ref 129157 - no_role_a no_role_b objectlinkcanvas 131845 norel geometry VH from ref 128005 z 1999 to point 68 150 line 131973 z 1999 to ref 128133 no_role_a no_role_b +objectlinkcanvas 132997 norel + geometry VHr + from ref 132869 z 1999 to point 194 343 + line 133253 z 1999 to ref 128389 + no_role_a no_role_b end From f8842c75ed4e4650fe58a8490267eb96363b9ebc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 11 Jul 2011 02:57:16 +0200 Subject: [PATCH 056/296] WIP draft OutputSlot internal transitions --- doc/devel/uml/fig145157.png | Bin 0 -> 13381 bytes src/proc/engine/buffer-provider.hpp | 8 ++ src/proc/play/output-slot.hpp | 10 ++- uml/lumiera/133637 | 52 ++++++++++- uml/lumiera/145157.diagram | 130 ++++++++++++++++++++++++++++ uml/lumiera/5.session | 4 +- uml/lumiera/lumiera.prj | 2 +- wiki/renderengine.html | 35 +++++++- 8 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 doc/devel/uml/fig145157.png create mode 100644 uml/lumiera/145157.diagram diff --git a/doc/devel/uml/fig145157.png b/doc/devel/uml/fig145157.png new file mode 100644 index 0000000000000000000000000000000000000000..dac8e23cb5105c210433a1973005e52182184529 GIT binary patch literal 13381 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 a0x#|Jr}tl~U#&vGOX;4fT#<}<(EkS=zo~@) literal 0 HcmV?d00001 diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 907cb9806..6b6429c21 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -51,6 +51,14 @@ namespace engine { + enum BufferState + { NIL, + FREE, + LOCKED, + EMITTED, + BLOCKED + }; + /** * Handle for a buffer for processing data, abstracting away the actual implementation. diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index d784bad85..e88d486fa 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -66,6 +66,7 @@ namespace play { /** established output channel */ class Connection; + typedef int64_t FrameNr; class DataSink @@ -73,7 +74,7 @@ namespace play { { public: - void emit(Time); + void emit(FrameNr); }; @@ -107,6 +108,13 @@ namespace play { Allocation allocate(); + protected: + friend class DataSink; + + virtual void lock (FrameNr, uint channel) =0; + virtual void transfer (FrameNr, uint channel) =0; + virtual void pushout (FrameNr, uint channel) =0; + private: }; diff --git a/uml/lumiera/133637 b/uml/lumiera/133637 index 6336ed674..9857cb0e0 100644 --- a/uml/lumiera/133637 +++ b/uml/lumiera/133637 @@ -1,6 +1,6 @@ format 58 "Play" // ProcessingLayer::Play - revision 2 + revision 3 modified_by 5 "hiv" // class settings //class diagram settings @@ -352,5 +352,55 @@ ${inlines} relations end end + + sequencediagram 145157 "output data exchange" + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default show_stereotype_properties default + overlapping_bars size A4 + end + + class 178437 "Client" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 178565 "DataSink" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 178693 "BufferProvider" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end end end diff --git a/uml/lumiera/145157.diagram b/uml/lumiera/145157.diagram new file mode 100644 index 000000000..f75dd4db5 --- /dev/null +++ b/uml/lumiera/145157.diagram @@ -0,0 +1,130 @@ +format 58 + +classinstance 128005 class_ref 178437 // Client + name "" xyz 53 4 2000 life_line_z 2000 +classinstance 128133 class_ref 176133 // OutputSlot + name "" xyz 281 5 2000 life_line_z 2000 +classinstance 128261 class_ref 178565 // DataSink + name "" xyz 140 28 2000 life_line_z 2000 +classinstance 128389 class_ref 178693 // BufferProvider + name "" xyz 463 5 2000 life_line_z 2000 +fragment 131845 "client side" + xyzwh 41 28 1995 156 279 +end +fragment 131973 "output implementation" + xyzwh 281 29 1995 262 277 +end +durationcanvas 128517 classinstance_ref 128005 // :Client + xyzwh 72 83 2010 11 52 +end +durationcanvas 129541 classinstance_ref 128005 // :Client + xyzwh 72 211 2010 11 41 +end +durationcanvas 129669 classinstance_ref 128261 // :DataSink + xyzwh 162 238 2010 11 25 +end +durationcanvas 129925 classinstance_ref 128133 // :OutputSlot + xyzwh 307 242 2010 11 63 + overlappingdurationcanvas 133637 + xyzwh 313 272 2020 11 28 + end +end +durationcanvas 130309 classinstance_ref 128389 // :BufferProvider + xyzwh 497 250 2010 11 26 +end +durationcanvas 131333 classinstance_ref 128133 // :OutputSlot + xyzwh 307 85 2010 11 34 +end +durationcanvas 132101 classinstance_ref 128261 // :DataSink + xyzwh 162 107 2010 11 25 +end +durationcanvas 132485 classinstance_ref 128005 // :Client + xyzwh 72 167 2010 11 27 +end +durationcanvas 132613 classinstance_ref 128261 // :DataSink + xyzwh 162 169 2010 11 25 +end +durationcanvas 132869 classinstance_ref 128133 // :OutputSlot + xyzwh 307 173 2010 11 25 +end +durationcanvas 133125 classinstance_ref 128005 // :Client + xyzwh 72 211 2010 11 29 +end +durationcanvas 133381 classinstance_ref 128389 // :BufferProvider + xyzwh 497 180 2010 11 25 +end +durationcanvas 133893 classinstance_ref 128389 // :BufferProvider + xyzwh 497 280 2010 11 25 +end +msg 129797 synchronous + from durationcanvas_ref 129541 + to durationcanvas_ref 129669 + yz 238 2015 explicitmsg "emit()" + show_full_operations_definition default drawing_language default + label_xy 109 230 +msg 130053 synchronous + from durationcanvas_ref 129669 + to durationcanvas_ref 129925 + yz 242 2020 explicitmsg "transfer()" + show_full_operations_definition default drawing_language default + label_xy 217 234 +msg 130437 synchronous + from durationcanvas_ref 129925 + to durationcanvas_ref 130309 + yz 250 2030 explicitmsg "transition to EMITTED" + show_full_operations_definition default drawing_language default + label_xy 374 243 +msg 131461 synchronous + from durationcanvas_ref 128517 + to durationcanvas_ref 131333 + yz 85 2015 explicitmsg "allocate()" + show_full_operations_definition default drawing_language default + label_xy 102 75 +msg 132229 synchronous + from durationcanvas_ref 131333 + to durationcanvas_ref 132101 + yz 108 2020 explicitmsg "create DataSink" + show_full_operations_definition default drawing_language default + label_xy 201 99 +msg 132357 return + from durationcanvas_ref 132101 + to durationcanvas_ref 128517 + yz 116 2025 explicitmsg "DataSink" + show_full_operations_definition default drawing_language default + label_xy 101 121 +msg 132741 synchronous + from durationcanvas_ref 132485 + to durationcanvas_ref 132613 + yz 169 2015 explicitmsg "lockBuffer()" + show_full_operations_definition default drawing_language default + label_xy 97 159 +msg 132997 synchronous + from durationcanvas_ref 132613 + to durationcanvas_ref 132869 + yz 175 2020 explicitmsg "lock()" + show_full_operations_definition default drawing_language default + label_xy 225 165 +reflexivemsg 133253 synchronous + to durationcanvas_ref 133125 + yz 211 2020 explicitmsg "generate Data" + show_full_operations_definition default drawing_language default + label_xy 84 199 +msg 133509 synchronous + from durationcanvas_ref 132869 + to durationcanvas_ref 133381 + yz 180 2025 explicitmsg "transition to LOCKED" + show_full_operations_definition default drawing_language default + label_xy 374 173 +reflexivemsg 133765 synchronous + to durationcanvas_ref 133637 + yz 272 2040 explicitmsg "pushout()" + show_full_operations_definition default drawing_language default + label_xy 325 260 +msg 134021 synchronous + from durationcanvas_ref 133637 + to durationcanvas_ref 133893 + yz 289 2025 explicitmsg "transition to FREE" + show_full_operations_definition default drawing_language default + label_xy 374 282 +preferred_whz 586 416 1 +end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 0e80581b8..87c7f7827 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -6,8 +6,10 @@ diagrams 730 488 100 4 0 0 classdiagram_ref 143877 // Player Entities 663 648 100 4 0 0 - active objectdiagram_ref 144005 // Play Process Structure + objectdiagram_ref 144005 // Play Process Structure 562 424 100 4 0 0 + active sequencediagram_ref 145157 // output data exchange + 586 416 100 4 0 0 end show_stereotypes selected diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index e9189265f..6c6cb4c64 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 58 "lumiera" - revision 70 + revision 71 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 1826d59ef..042b26d15 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3324,7 +3324,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3393,8 +3393,37 @@ This data exchange protocol operates on a rather low level; there is only limite
 
 Thus there are two serious problem situations
 * allocating the buffer out of time window bears the danger of output data corruption; but the general assumption is for the scheduler to ensure each job start time remains within the defined window and all prerequisite jobs have terminated successfully. To handle clean-up, we additionally need special jobs running always, in order, and be notified of prerequisite failures.
-* failure to release a buffer timely blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole.
-&rarr; SchedulerRequirements
+* failure to release a buffer in a timely fashion blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole. +&rarr; SchedulerRequirements +&rarr; OutputSlotImpl +
+
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot is outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
+
+!operation steps
+[>img[Sequenz of output data exchange steps|uml/fig145157.png]]The OutputSlot class defines some standard operations as protected virtual functions. These represent the actual steps in the data exchange protocol corresponding to this output slot.
+;lock()
+:claims the specified buffer for exclusive use.
+:Client may now dispose output data
+;transfer()
+:transfers control from the client to the actual output connection for feeding data.
+:Triggered by the client invoking {{{emit()}}}
+;pushout()
+:request the actual implementation to push the data to output.
+:Usually invoked from the {{{transfer()}}} implementation, alternatively from a separate thread.
+
+
+!buffer states
+While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the client (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
+|        !state||>| !lock()   |>| !transfer() |>| !pushout() |
+|     {{{NIL}}}||↯| ·        |↯|             | ↯ |          |
+|    {{{FREE}}}||✔|↷ locked  |✔|· (ignore)  | · | ·         |
+|  {{{LOCKED}}}||↯|↷ emitted |✔|↷ emitted   | · |↷ free    |
+| {{{EMITTED}}}||↯|↷ blocked |↯|↷ blocked   | · |↷ free    |
+| {{{BLOCKED}}}||↯| ✗        |↯| ✗          | ∅ |↷ free    |
+where · means no operation, ✔ marks the standard cases (OK response to caller), ↯ will throw and thus kill the calling job, ∅ will treat this frame as //glitch,// ✗ will request playback stop.
+The rationale is for all states out-of-order to transition into the {{{BLOCKED}}}-state eventually, which, when hit by the next operation, will request playback stop.
+
The Lumiera Processing Layer is comprised of various subsystems and can be separated into a low-level and a high-level part. At the low-level end is the [[Render Engine|OverviewRenderEngine]] which basically is a network of render nodes cooperating closely with the Backend Layer in order to carry out the actual playback and media transforming calculations. Whereas on the high-level side we find several different [[Media Objects|MObjects]] that can be placed into the session, edited and manipulated. This is complemented by the [[Asset Management|Asset]], which is the "bookkeeping view" of all the different "things" within each [[Session|SessionOverview]].

From 4a62444ad439e2492dc82d744e74a03ab2c4efbf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 14 Aug 2011 03:09:05 +0200
Subject: [PATCH 057/296] WIP pick up on the design work regarding Engine,
 OutputSlot and Player

---
 src/proc/engine/calc-stream.hpp    | 91 ++++++++++++++++++++++++++++++
 src/proc/engine/engine-service.hpp | 30 ++++++----
 src/proc/engine/renderengine.hpp   |  5 ++
 src/proc/engine/rendergraph.hpp    |  4 ++
 wiki/renderengine.html             |  8 +--
 5 files changed, 124 insertions(+), 14 deletions(-)
 create mode 100644 src/proc/engine/calc-stream.hpp

diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp
new file mode 100644
index 000000000..f026b56d2
--- /dev/null
+++ b/src/proc/engine/calc-stream.hpp
@@ -0,0 +1,91 @@
+/*
+  CALC-STREAM.hpp  -  abstraction representing a series of scheduled calculations
+
+  Copyright (C)         Lumiera.org
+    2011,               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_ENGINE_CALC_STREAM_H
+#define PROC_ENGINE_CALC_STREAM_H
+
+
+//#include "include/dummy-player-facade.h"
+//#include "include/display-facade.h"
+//#include "common/instancehandle.hpp"
+//#include "lib/singleton-ref.hpp"
+//
+//#include 
+//#include 
+//#include 
+
+
+namespace proc {
+namespace play {
+
+//    using std::string;
+//    using lumiera::Subsys;
+//    using lumiera::Display;
+//    using lumiera::DummyPlayer;
+  
+  
+  
+  
+  
+  /*********************************************************
+   * A calculation stream groups and abstracts a series of
+   * calculation jobs, delivering frames into the configured
+   * OutputSlot in a timely fashion. Behind the scenes, this
+   * gets translated into several jobs enqueued with the
+   * scheduler in the backend layer. The calculation stream
+   * implementation cares to create and configure these
+   * jobs and to manage the necessary dependencies and
+   * callbacks.
+   * 
+   * Regarding the implementation, a CalcStream is an const
+   * value object holding the metadata necessary to manage
+   * the underlying jobs. The only way to create a CalcStream
+   * properly is to retrieve it from the  factory functions
+   * of the EngineService. At that point, the corresponding
+   * jobs will be configured and enqueued. 
+   */
+  class CalcStream
+    {
+      
+      friend class EngineService;
+      
+      CalcStream()
+        {
+          
+        }
+      
+    public:
+      CalcStream (CalcStream const& o)
+        { }
+      
+     ~CalcStream() { }
+      
+    };
+  
+  
+  
+  
+} // namespace play
+} // namespace proc
+#endif
diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp
index 7798307b0..b7babf91a 100644
--- a/src/proc/engine/engine-service.hpp
+++ b/src/proc/engine/engine-service.hpp
@@ -21,10 +21,14 @@
 */
 
 /** @file engine-service.hpp
- ** A public service provided by
- **
- ** @see lumiera::DummyPlayer
- ** @see gui::PlaybackController usage example 
+ ** Access point for the (core) calculation service of the render engine.
+ ** This public service is provided by the Proc-Layer, but actually implemented
+ ** using backend services (especially the scheduler). The central concept provided
+ ** through this facade interface is that of a calculation stream. On the
+ ** implementation side, these get translated into a series of jobs invoking
+ ** render nodes, to be invoked through the scheduler in the backend layer. 
+ ** 
+ ** @see proc::play::PlayerService
  */
 
 
@@ -34,6 +38,7 @@
 
 //#include "include/dummy-player-facade.h"
 //#include "include/display-facade.h"
+#include "proc/engine/calc-stream.hpp"
 //#include "common/instancehandle.hpp"
 //#include "lib/singleton-ref.hpp"
 //
@@ -55,12 +60,14 @@ namespace play {
   
   
   /******************************************************
-   * Actual implementation of the DummyPlayer service.
-   * Creating an instance of this class automatically
-   * registers the interface lumieraorg_DummyPlayer with
-   * the Lumiera Interface/Plugin system and creates
-   * a forwarding proxy within the application core to
-   * route calls through this interface.
+   * A service to schedule series of calculations,
+   * delivering the rendered data into an external output
+   * sink in a timely fashion. Actually the CalculationStream
+   * instances provided through this (facade) interface are
+   * backed by jobs executed through the scheduler in the
+   * backend layer. The implementation of this service
+   * cares to create the right job entries in the correct
+   * order and to enqueue these into the scheduler.
    */
   class EngineService
     : boost::noncopyable
@@ -84,6 +91,9 @@ namespace play {
       
      ~EngineService() { } /////TODO notifyTermination_(&error_); }
       
+      CalcStream
+      
+      
     };
   
   
diff --git a/src/proc/engine/renderengine.hpp b/src/proc/engine/renderengine.hpp
index c3030271f..ebbd46e5a 100644
--- a/src/proc/engine/renderengine.hpp
+++ b/src/proc/engine/renderengine.hpp
@@ -31,6 +31,11 @@
 
 using std::list;
 
+/////////////////////////////TODO 7/11 this is a piece of debris, left over from the first attempt to complete the render nodes network.
+/////////////////////////////TODO Meanwhile the intention is to treat the render nodes network more like a data structure,
+/////////////////////////////TODO consequently this will become some kind of root or anchor point for this network
+
+//////////TODO for the "real" engine API: look at engine-serivce.hpp
 
 namespace engine {
   
diff --git a/src/proc/engine/rendergraph.hpp b/src/proc/engine/rendergraph.hpp
index a77eac68b..eb811de6f 100644
--- a/src/proc/engine/rendergraph.hpp
+++ b/src/proc/engine/rendergraph.hpp
@@ -29,6 +29,10 @@
 #include "lib/time/timevalue.hpp"
 
 
+/////////////////////////////TODO 7/11 this is a piece of debris, left over from the first attempt to complete the render nodes network.
+/////////////////////////////TODO Meanwhile the intention is to treat the render nodes network more like a data structure,
+/////////////////////////////TODO consequently this will become some kind of root or anchor point for this network
+
 
 namespace engine {
   
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 042b26d15..2c0859c66 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1785,10 +1785,10 @@ To make the intended use of the classes more clear, consider the following two e
 * a video clip placed relatively, with an attached HUE effect &rarr;[[Example2]]
 
-
+
a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
 
-The render nodes network is always buils separate for each [[timeline segment|Segmentation]], which is //constant in wiring configuration.// Thus, while exit node(s) are per segment, the corresponding exit nodes of consecutive segments together belong to a ModelPort, which in turn corresponds to a global pipe (master bus not connected any further). These relations guide the possible configuration for an exit node: It may still provide multiple channels -- but all those channels are bound to belong to a single logical stream -- same StreamPrototype, always handled as bundle, connected and routed in one step. For example, when there is an 5.1 Audio master bus with a single fader, then "5.1 Audio" would be a prototype and these 6 channels will always be handled together; in such a case it makes perfectly sense to access these 6 audio channels through a single exit node, which is keyed (identified) by the same [[Pipe]]-ID as the corresponding ModelPort and the corresponding global pipe ("5.1 Audio master bus")
+The render nodes network is always built separate for each [[timeline segment|Segmentation]], which is //constant in wiring configuration.// Thus, while exit node(s) are per segment, the corresponding exit nodes of consecutive segments together belong to a ModelPort, which in turn corresponds to a global pipe (master bus not connected any further). These relations guide the possible configuration for an exit node: It may still provide multiple channels -- but all those channels are bound to belong to a single logical stream -- same StreamPrototype, always handled as bundle, connected and routed in one step. For example, when there is an 5.1 Audio master bus with a single fader, then "5.1 Audio" would be a prototype and these 6 channels will always be handled together; in such a case it makes perfectly sense to access these 6 audio channels through a single exit node, which is keyed (identified) by the same [[Pipe]]-ID as the corresponding ModelPort and the corresponding global pipe ("5.1 Audio master bus")
 
@@ -3397,7 +3397,7 @@ Thus there are two serious problem situations &rarr; SchedulerRequirements &rarr; OutputSlotImpl
-
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot is outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
 
 !operation steps
@@ -3414,7 +3414,7 @@ Thus there are two serious problem situations
 
 
 !buffer states
-While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the client (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
+While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the server side (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
 |        !state||>| !lock()   |>| !transfer() |>| !pushout() |
 |     {{{NIL}}}||↯| ·        |↯|             | ↯ |          |
 |    {{{FREE}}}||✔|↷ locked  |✔|· (ignore)  | · | ·         |

From 1e7da409bb0a24531673dd7a08046b6b3cfb62b9 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 15 Aug 2011 00:11:12 +0200
Subject: [PATCH 058/296] Fix logic error uncovered by GCC 4.4

benefits of using a newer compiler, yay!

Explanation: the Link<...> template combines
various policy templates and exposes a set
of functions, which can be bound as functor
into an concrete time::Control instance.

Actually, we need to mix in only the Mutation
baseclass, because we just want to inherit
the privilege to change time values, which
are otherwise immutable. We don't need to
mix in the Mutator template anymore (this
was a leftover from an earlier design)
---
 src/lib/time/control-policy.hpp | 8 ++++----
 src/lib/time/control.hpp        | 2 ++
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/lib/time/control-policy.hpp b/src/lib/time/control-policy.hpp
index a941c3c96..7ffed6426 100644
--- a/src/lib/time/control-policy.hpp
+++ b/src/lib/time/control-policy.hpp
@@ -234,7 +234,7 @@ namespace mutation {
    */
   template
   struct Link
-    : Mutator
+    : Mutation
     , Builder
     {
       
@@ -255,15 +255,15 @@ namespace mutation {
       static TI
       mutateLength (TimeSpan& target, Duration const& change)
         {
-          Mutator::imposeChange (target.duration(), change);
+          imposeChange (target.duration(), change);
           return Builder::buildChangedValue(target);
         }
       
       static TimeSpan
       mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
         {
-          Mutator::imposeChange (target.duration(), change.duration());
-          Mutator::imposeChange (target,change.start());
+          imposeChange (target.duration(), change.duration());
+          imposeChange (target,change.start());
           return Builder::buildChangedValue(target);
         }
       
diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index cbac65d33..486097934 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -94,6 +94,8 @@
  **   the change, which might or might not reflect the change value passed in.
  ** 
  ** @todo include support for QuTimeSpan values                               ////////////////////TICKET #760
+ ** 
+ ** @see TimeControl_test
  **
  */
 

From 2625eee07cd1b7d25cc46bbd3be924d16d758c5a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 15 Aug 2011 00:12:13 +0200
Subject: [PATCH 059/296] disable work-in-progress to make the tree compile

TODO noted as Ticket #819
---
 tests/components/proc/play/output-slot-protocol-test.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp
index c83d6bff3..124d49372 100644
--- a/tests/components/proc/play/output-slot-protocol-test.cpp
+++ b/tests/components/proc/play/output-slot-protocol-test.cpp
@@ -68,6 +68,7 @@ namespace test  {
       void
       verifyStandardCase()
         {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819
           // Create Test fixture.
           // In real usage, the OutputSlot will be preconfigured
           // (Media format, number of channels, physical connections)
@@ -135,6 +136,7 @@ namespace test  {
           CHECK (*stream1++ == testData[1,0]);
           CHECK (*stream1++ == testData[1,1]);
           CHECK (!stream1);
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819
         }
     };
   

From d1b6f7a57b9205cfea1874e75e5d9d9d48025773 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 15 Aug 2011 00:52:12 +0200
Subject: [PATCH 060/296] polymorphic value builder functions: how to take
 arguments

Previously, I've added an additional '&' to be able
to pass references without much ado. This turned out
to be problematic when using constant values at the
invocation site. Well. C++ has really a fixation
ont passing things by value. This is fine, but
doesn't play so well at times when passing smart-ptrs
or similar ref-counting stuff, especially when we just
want to *use* the handle, not store it away.
Essentially the same situation as with for_each

Bottom line: from now on, we need to state the
template parameter for such arguments explicitly,
or just accept the overhead of creating an additional
transient copy of the smart-ptr.
---
 src/lib/polymorphic-value.hpp | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp
index 0f13a9b98..a05e04519 100644
--- a/src/lib/polymorphic-value.hpp
+++ b/src/lib/polymorphic-value.hpp
@@ -389,21 +389,21 @@ namespace lib {
         }
       
       template
-      PolymorphicValue (IMP*, A1& a1)
+      PolymorphicValue (IMP*, A1 a1)
         {
           REQUIRE (siz >= sizeof(IMP));
           new(&buf_) IMP (a1);
         }
       
       template
-      PolymorphicValue (IMP*, A1& a1, A2& a2)
+      PolymorphicValue (IMP*, A1 a1, A2 a2)
         {
           REQUIRE (siz >= sizeof(IMP));
           new(&buf_) IMP (a1,a2);
         }
       
       template
-      PolymorphicValue (IMP*, A1& a1, A2& a2, A3& a3)
+      PolymorphicValue (IMP*, A1 a1, A2 a2, A3 a3)
         {
           REQUIRE (siz >= sizeof(IMP));
           new(&buf_) IMP (a1,a2,a3);
@@ -444,13 +444,13 @@ namespace lib {
           Adapter() : IMP() { }
           
           template
-          Adapter (A1& a1) : IMP(a1) { }
+          Adapter (A1 a1) : IMP(a1) { }
           
           template
-          Adapter (A1& a1, A2& a2) : IMP(a1,a2) { }
+          Adapter (A1 a1, A2 a2) : IMP(a1,a2) { }
           
           template
-          Adapter (A1& a1, A2& a2, A3& a3) : IMP(a1,a2,a3) { }
+          Adapter (A1 a1, A2 a2, A3 a3) : IMP(a1,a2,a3) { }
           
           /* using default copy and assignment */
         };
@@ -505,31 +505,31 @@ namespace lib {
       static PolymorphicValue
       build ()
         {
-          Adapter* type_to_build_in_buffer;
+          Adapter* type_to_build_in_buffer(0);
           return PolymorphicValue (type_to_build_in_buffer);
         }
       
       template
       static PolymorphicValue
-      build (A1& a1)
+      build (A1 a1)
         {
-          Adapter* type_to_build_in_buffer;
+          Adapter* type_to_build_in_buffer(0);
           return PolymorphicValue (type_to_build_in_buffer, a1);
         }
       
       template
       static PolymorphicValue
-      build (A1& a1, A2& a2)
+      build (A1 a1, A2 a2)
         {
-          Adapter* type_to_build_in_buffer;
+          Adapter* type_to_build_in_buffer(0);
           return PolymorphicValue (type_to_build_in_buffer, a1,a2);
         }
       
       template
       static PolymorphicValue
-      build (A1& a1, A2& a2, A3& a3)
+      build (A1 a1, A2 a2, A3 a3)
         {
-          Adapter* type_to_build_in_buffer;
+          Adapter* type_to_build_in_buffer(0);
           return PolymorphicValue (type_to_build_in_buffer, a1,a2,a3);
         }
       

From 3125d1c5735139bea31d51bbe4d344cde986ae69 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 15 Aug 2011 00:52:56 +0200
Subject: [PATCH 061/296] a first draft for the Engine Interface

---
 src/proc/engine/calc-stream.hpp    | 11 ++--
 src/proc/engine/engine-service.cpp | 93 +++++++++++++++++++++++++++---
 src/proc/engine/engine-service.hpp | 57 +++++++++++++++++-
 src/proc/play/output-slot.hpp      |  1 +
 src/proc/play/timings.hpp          | 89 ++++++++++++++++++++++++++++
 wiki/renderengine.html             | 36 ++++++------
 6 files changed, 255 insertions(+), 32 deletions(-)
 create mode 100644 src/proc/play/timings.hpp

diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp
index f026b56d2..b9b32c9fc 100644
--- a/src/proc/engine/calc-stream.hpp
+++ b/src/proc/engine/calc-stream.hpp
@@ -26,6 +26,7 @@
 #define PROC_ENGINE_CALC_STREAM_H
 
 
+#include "lib/error.hpp"
 //#include "include/dummy-player-facade.h"
 //#include "include/display-facade.h"
 //#include "common/instancehandle.hpp"
@@ -36,8 +37,8 @@
 //#include 
 
 
-namespace proc {
-namespace play {
+namespace proc  {
+namespace engine{
 
 //    using std::string;
 //    using lumiera::Subsys;
@@ -77,7 +78,9 @@ namespace play {
       
     public:
       CalcStream (CalcStream const& o)
-        { }
+        { 
+          UNIMPLEMENTED("build a calculation stream");
+        }
       
      ~CalcStream() { }
       
@@ -86,6 +89,6 @@ namespace play {
   
   
   
-} // namespace play
+} // namespace engine
 } // namespace proc
 #endif
diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp
index 23078f68c..40531d4a5 100644
--- a/src/proc/engine/engine-service.cpp
+++ b/src/proc/engine/engine-service.cpp
@@ -40,14 +40,93 @@ namespace engine{
 //    using std::tr1::bind;
     
     
-    namespace { // hidden local details of the service implementation....
+  namespace { // hidden local details of the service implementation....
+    
+  } // (End) hidden service impl details
+    
+    
+  
+    
+  /** */  
+  EngineService::EngineService()
+    { }
       
-    } // (End) hidden service impl details
-    
-    
-    
-    
-    /** */  
+      
+  
+  /** */
+  CalcStream
+  EngineService::calculate(ModelPort mPort,
+                           Timings nominalTimings,
+                           OutputConnection output,
+                           Quality serviceQuality)
+  {
+    UNIMPLEMENTED ("build a standard calculation stream");
+  }
+  
+  
+  
+  /** */
+  CalcStream
+  EngineService::calculateBackground(ModelPort mPort,
+                                     Timings nominalTimings,
+                                     Quality serviceQuality)
+  {
+    UNIMPLEMENTED ("build a calculation stream for background rendering");
+  }
+  
+  
+  
+  /* ===== Quality-of-Service ===== */
+  
+  
+  EngineService::Quality::~Quality() { } // emit vtables here...
+  
+  enum CalcType {
+    PLAYBACK,
+    RENDER,
+    BACKGROUND
+  };
+  
+  
+  class DefaultQoS
+    : public EngineService::Quality
+    {
+      CalcType type_;
+      
+    public:
+      DefaultQoS (CalcType type)
+        : type_(type)
+        { }
+    };
+  
+  class PriorityQoS
+    : public DefaultQoS
+    {
+      CalcType type_;
+      
+    public:
+      PriorityQoS ()
+        : DefaultQoS(PLAYBACK)
+        { }
+    };
+  
+  class Compromise
+    : public DefaultQoS
+    {
+      
+    public:
+      Compromise (CalcType type)
+        : DefaultQoS(type)
+        { }
+    };
+  
+  
+  EngineService::QoS_Definition  EngineService::QoS_DEFAULT         = QoS_Definition::build (PLAYBACK);
+  EngineService::QoS_Definition  EngineService::QoS_BACKGROUND      = QoS_Definition::build (BACKGROUND);
+  EngineService::QoS_Definition  EngineService::QoS_COMPROMISE      = QoS_Definition::build (PLAYBACK); 
+  EngineService::QoS_Definition  EngineService::QoS_PERFECT_RESULT  = QoS_Definition::build (RENDER);
+  EngineService::QoS_Definition  EngineService::QoS_SYNC_PRIORITY   = QoS_Definition::build();
+  
   
   
 }} // namespace proc::engine
diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp
index b7babf91a..502e63329 100644
--- a/src/proc/engine/engine-service.hpp
+++ b/src/proc/engine/engine-service.hpp
@@ -36,24 +36,33 @@
 #define PROC_ENGINE_ENGINE_SERVICE_H
 
 
+#include "lib/error.hpp"
 //#include "include/dummy-player-facade.h"
 //#include "include/display-facade.h"
 #include "proc/engine/calc-stream.hpp"
+#include "proc/mobject/model-port.hpp"
+#include "proc/play/timings.hpp"
+#include "proc/play/output-slot.hpp"
 //#include "common/instancehandle.hpp"
 //#include "lib/singleton-ref.hpp"
+#include "lib/polymorphic-value.hpp"
 //
 #include 
 //#include 
 //#include 
 
 
-namespace proc {
-namespace play {
+namespace proc  {
+namespace engine{
 
 //    using std::string;
 //    using lumiera::Subsys;
 //    using lumiera::Display;
 //    using lumiera::DummyPlayer;
+  using mobject::ModelPort;
+  using proc::play::Timings;
+  
+  typedef proc::play::OutputSlot::Allocation OutputConnection;
   
   
   
@@ -86,19 +95,61 @@ namespace play {
 //    lib::SingletonRef implInstance_;
 //    ServiceInstanceHandle serviceInstance_;
       
+      /* The following typedefs allow to hand out predefined
+       * Quality-of-Service strategy definitions as value objects,
+       * without disclosing implementation details here in this header.
+       */
+      enum{ QoS_IMPL_SIZE = sizeof(size_t) };  /////////////////////////////////////////TODO is this correct??
+      
+      
     public:
+      /*************************************************************
+       * Quality-of-Service definition for an Render Engine usage.
+       * This strategy defines how to decide between conflicting goals like
+       * - timely delivery
+       * - image quality
+       * - niceness and resource usage
+       */
+      class Quality
+        {
+          public:
+            virtual ~Quality();  ///< this is an Interface
+        };
+      
+      
+//      typedef lib::polyvalue::CloneValueSupport _ClonableQoS_Strategy;
+      typedef lib::PolymorphicValue QoS_Definition;
+      
+      static QoS_Definition  QoS_DEFAULT;
+      static QoS_Definition  QoS_BACKGROUND;
+      static QoS_Definition  QoS_SYNC_PRIORITY;
+      static QoS_Definition  QoS_PERFECT_RESULT;
+      static QoS_Definition  QoS_COMPROMISE;
+      
+      
+      
       EngineService();    /////TODO (Subsys::SigTerm terminationHandle);
       
      ~EngineService() { } /////TODO notifyTermination_(&error_); }
       
       CalcStream
+      calculate(ModelPort mPort,
+                Timings nominalTimings,
+                OutputConnection output,
+                Quality serviceQuality =QoS_DEFAULT);
       
+      CalcStream
+      calculateBackground(ModelPort mPort,
+                          Timings nominalTimings,
+                          Quality serviceQuality =QoS_BACKGROUND);
       
     };
   
   
   
   
-} // namespace play
+  
+  
+} // namespace engine
 } // namespace proc
 #endif
diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp
index e88d486fa..b3188724e 100644
--- a/src/proc/play/output-slot.hpp
+++ b/src/proc/play/output-slot.hpp
@@ -40,6 +40,7 @@
 #include "lib/handle.hpp"
 #include "lib/time/timevalue.hpp"
 #include "proc/engine/buffer-provider.hpp"
+#include "proc/play/timings.hpp"
 #include "lib/iter-source.hpp"
 //#include "lib/sync.hpp"
 
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
new file mode 100644
index 000000000..a3613bbf9
--- /dev/null
+++ b/src/proc/play/timings.hpp
@@ -0,0 +1,89 @@
+/*
+  TIMINGS.hpp  -  timing specifications for a frame quantised data stream
+
+  Copyright (C)         Lumiera.org
+    2011,               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 timings.hpp
+ ** How to define a timing specification or constraint.
+ ** A timing spec is used to anchor an data stream with relation to a time axis or frame grid.
+ ** There are two kinds of timing specs:
+ ** - nominal timing specifications relate to the nominal time values
+ **   of the frames in a data stream, i.e. the "should be" time values.
+ **   These might be values derived from a timecode or just values in
+ **   in relation to the timeline axis, but without any connection to
+ **   the real wall clock time
+ ** - actual timing specifications are always connected or related to
+ **   an external time source, typically just wall clock time. For example,
+ **   actual timing specs dictate the constraints for real time frame
+ **   delivery to an external output connection.
+ **   
+ ** @todo WIP-WIP-WIP 8/2011
+ ** @see output-slot.hpp  ////TODO
+ */
+
+
+#ifndef PROC_PLAY_TIMINGS_H
+#define PROC_PLAY_TIMINGS_H
+
+
+#include "lib/error.hpp"
+//#include "lib/handle.hpp"
+#include "lib/time/timevalue.hpp"
+//#include "proc/engine/buffer-provider.hpp"
+//#include "lib/iter-source.hpp"
+//#include "lib/sync.hpp"
+
+//#include 
+//#include 
+//#include 
+//#include 
+//#include 
+
+
+namespace proc {
+namespace play {
+
+  using lib::time::Time;
+//using std::string;
+
+//using std::vector;
+//using std::tr1::shared_ptr;
+//using boost::scoped_ptr;
+  
+  
+  
+  
+  /********************************************************************
+   * Data-Record: Generic frame timing specification.
+   * 
+   * @note copyable value class
+   * 
+   * @todo write type comment
+   */
+  class Timings
+    {
+    public:
+      //////////////TODO accessor functions here
+    };
+  
+  
+  
+}} // namespace proc::play
+#endif
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 2c0859c66..49a1a6096 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1643,7 +1643,7 @@ The main tool used to implement this separation is the [[Builder Pattern|http://
 Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
 
-
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
 
 !Starting point
@@ -1659,7 +1659,7 @@ Introduce a new kind of MObject, a GeneratorMO, to represent what the current du
 Reworking the dummy player into the [[Player subsystem|PlayService]] is a larger undertaking, and best broken up into several //stages.//
 # just moving the existing entities into the final location (typically down in the layer hierarchy)
 # introduce a GeneratorMO and a GeneratorNode, while providing a shortcut to get the GeneratorNode without requiring the (not yet implemented) Builder.
-# rework the ~ProcessImpl and the ~TickService to become the CalculationStream and use the real Engine Interface, but still with an mock implementation hooked up behind
+# rework the ~ProcessImpl and the ~TickService to become the [[calculation stream|CalcStream]] and use the real Engine Interface, but still with an mock implementation hooked up behind
 # extend and elaborate the handling of output, build the foundation of the real OutputManager
 # switch to the real Scheduler and Engine implementation
 
@@ -1734,11 +1734,11 @@ When building the low-level model, the actual processing code is resolved and a
 Initially, only the parameter (descriptors) are present on the effect ~MObject, while the actual [[parameter providers|ParamProvider]] are created or wired (by the ConManager) on demand.
 
-
-
The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|CalculationStream]].
+
+
The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|CalcStream]].
 
 Below this facade, there is a thin adaptadion and forwarding layer, mainly talking to
-* the individual CalculationStream elements created for each PlayProcess.
+* the individual [[Calculation Streams|CalcStream]] created for each PlayProcess.
 * the FrameDispatcher, which translates such streams into a series of RenderJob entries
 * the [[Scheduler]], which is responsible to perform these jobs in a timely fashion
 
@@ -1916,25 +1916,25 @@ To support this usage pattern, the Fixture implementation makes use of the [[PIm * moreover, this necessitates a tight integration down to implementation level, both with the clean-up and the render processes themselves
-
+
The Fixture &rarr; [[data structure|FixtureDatastructure]] acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel).
-Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes are known to be terminated.
+Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes (&rarr; CalcStream) are known to be terminated.
 
 Each segment owns an AllocationCluster, which in turn manages all the numerous small-sized objects comprising the render network implementing this segment -- thus the central question is when to //release the segment.//
-* for one, we easily detect the point when a segment is swapped out of the segmentation; at this point we also have to detect the //tainted render processes.//
-* but those render processes terminate asynchronously, and that forces us to do some kind of registration and deregistration.
+* for one, we easily detect the point when a segment is swapped out of the segmentation; at this point we also have to detect the //tainted calculation streams.//
+* but those render processes (calc streams) terminate asynchronously, and that forces us to do some kind of registration and deregistration.
 
 !!question: is ref-counting acceptable here?
 //Not sure yet.// Of course it would be the simplest approach. KISS.
-Basically the concern is that each new render process had to access the shared counts of all segments it touches.
+Basically the concern is that each new CalcStream had to access the shared counts of all segments it touches.
 
 ''Note'': {{{shared_ptr}}} is known to be implemented by a lock-free algorithm (yes it is, at least since boost 1.33. Don't believe what numerous FUD spreaders have written). Thus lock contention isn't a problem, but at least a memory barrier is involved (and if I&nbsp;judge GCC's internal documentation right, currently their barriers extend to //all// globally visible variables)
 
 !!question: alternatives?
-There are. As the builder is known to be run again and again, no one forces us to deallocate as soon as we could. That's the classical argument exploited by any garbage collector too. Thus we could just note the fact that a render process had terminated and evaluate all those noted results on later occasion.
+There are. As the builder is known to be run again and again, no one forces us to deallocate as soon as we could. That's the classical argument exploited by any garbage collector too. Thus we could just note the fact that a calculation stream is done and re-evaluate all those noted results on later occasion.
 
 !!exploiting the frame-dispatch step
-Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
+Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process / a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
 
 !!assessment {{red{WIP 12/10}}}
 But the primary question here is to judge the impact of such an implementation. What would be the costs?
@@ -2008,8 +2008,8 @@ Additionally, they may be used for resource management purposes by embedding a r
 #* one OpenGL Dataframe could contain raw texture data (but I am lacking expertise for this topic)
 
-
-
An implementation facility within the RenderEngine, responsible for translating a logical CalculationStream (corresponding to a PlayProcess) into a sequence of individual RenderJob entries, which can then be handed over to the [[Scheduler]]. Performing this operation involves a special application of [[time quantisation|TimeQuant]]: after establishing a suitable starting point, a typically contiguous series of frame numbers need to be generated, together with the time coordinates for each of those frames
+
+
An implementation facility within the RenderEngine, responsible for translating a logical [[calculation stream|CalcStream]] (corresponding to a PlayProcess) into a sequence of individual RenderJob entries, which can then be handed over to the [[Scheduler]]. Performing this operation involves a special application of [[time quantisation|TimeQuant]]: after establishing a suitable starting point, a typically contiguous series of frame numbers need to be generated, together with the time coordinates for each of those frames
/***
@@ -4283,12 +4283,12 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T
 <<tasksum end>>
 
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
 The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
-''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalculationStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
-* the [[Backend]] is responsible for scheduling and [[dispatching|Dispatcher]] the CalculationStream
+''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalcStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
+* the [[Backend]] is responsible for scheduling and [[dispatching|Dispatcher]] the [[calculation stream|CalcStream]]
 * the RenderEngine has the ability to cary out individual frame calculations
 * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
 
@@ -4296,7 +4296,7 @@ The play process is an conceptual entity linking together several activities in
 !Anatomy of a Play Process
 The Controller is exposed to the client and acts as frontend handle, while the play process body groups and manages all the various parts cooperating to generate output. For each of the participating global pipes we get a feed to drive that pipeline to deliver media of a specific kind.
 
-Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the current collection of individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, wired into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalculationStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalculationStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
+Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the current collection of individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, wired into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalcStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalcStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
 
From 737765260d9a4e609a41441ddbb4ac73b06a9b58 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 16 Aug 2011 03:02:55 +0200 Subject: [PATCH 062/296] Test-driven brainstorming: diagnostic adaptor for the engine --- src/proc/engine/engine-diagnostics.hpp | 105 +++++++++++++++++ src/proc/engine/engine-service.cpp | 18 +-- src/proc/engine/engine-service.hpp | 54 +++++---- tests/46engine.tests | 8 ++ tests/47playout.tests | 7 ++ .../proc/engine/calc-stream-test.cpp | 76 ++++++++++++ .../proc/engine/engine-interface-test.cpp | 111 ++++++++++++++++++ wiki/renderengine.html | 4 +- 8 files changed, 353 insertions(+), 30 deletions(-) create mode 100644 src/proc/engine/engine-diagnostics.hpp create mode 100644 tests/47playout.tests create mode 100644 tests/components/proc/engine/calc-stream-test.cpp create mode 100644 tests/components/proc/engine/engine-interface-test.cpp diff --git a/src/proc/engine/engine-diagnostics.hpp b/src/proc/engine/engine-diagnostics.hpp new file mode 100644 index 000000000..bbb4052fb --- /dev/null +++ b/src/proc/engine/engine-diagnostics.hpp @@ -0,0 +1,105 @@ +/* + ENGINE-DIAGNOSTICS.hpp - diagnostic facility to investigate engine operation + + Copyright (C) Lumiera.org + 2011, 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 engine-diagnostics.hpp + ** An facility to check and monitor engine operations. + ** Once created, an EngineDiagnostics object connects to the EngineService + ** interface to activate additional tracing facilities within the engine, + ** allowing to watch and verify the creation of individual jobs and further + ** engine state parameters. + ** + ** @see EngineInterface + ** @see engine-interface-test.cpp + ** @see calc-stream-test.cpp + */ + + +#ifndef PROC_ENGINE_ENGINE_DIAGNOSTICS_H +#define PROC_ENGINE_ENGINE_DIAGNOSTICS_H + + +#include "lib/error.hpp" +#include "proc/engine/engine-service.hpp" +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "proc/engine/calc-stream.hpp" +//#include "proc/mobject/model-port.hpp" +//#include "proc/play/timings.hpp" +//#include "proc/play/output-slot.hpp" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +//#include "lib/polymorphic-value.hpp" +//#include "lib/singleton.hpp" +// +#include +//#include +//#include + + +namespace proc { +namespace engine{ + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + + + + + + /****************************************************** + * Render engine diagnostic facility. Creating an instance + * will activate additional tracing facilities within the + * render engine; results may be investigated through + * EngineDiagnostics public functions. The object acts + * like a smart handle, i.e. the tracing facilities will + * be disabled and disconnected when going out of scope. + */ + class EngineDiagnostics + : boost::noncopyable + { + EngineService& engine_; + + public: + EngineDiagnostics (EngineService& e) + : engine_(e) + { + UNIMPLEMENTED ("attach tracing connector"); + engine_.activateTracing(); + } + + ~EngineDiagnostics() + { + TODO ("detach tracing connector"); + engine_.disableTracing(); + } + }; + + + + + + +} // namespace engine +} // namespace proc +#endif diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp index 40531d4a5..b85353211 100644 --- a/src/proc/engine/engine-service.cpp +++ b/src/proc/engine/engine-service.cpp @@ -38,20 +38,24 @@ namespace engine{ // using std::auto_ptr; // using boost::scoped_ptr; // using std::tr1::bind; - - + + namespace { // hidden local details of the service implementation.... } // (End) hidden service impl details - - - + + + /** storage for the EngineService interface object */ + lib::Singleton EngineService::instance; + + + /** */ EngineService::EngineService() { } - - + + /** */ CalcStream diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index 502e63329..6b0d84e5f 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -22,12 +22,24 @@ /** @file engine-service.hpp ** Access point for the (core) calculation service of the render engine. - ** This public service is provided by the Proc-Layer, but actually implemented - ** using backend services (especially the scheduler). The central concept provided - ** through this facade interface is that of a calculation stream. On the - ** implementation side, these get translated into a series of jobs invoking - ** render nodes, to be invoked through the scheduler in the backend layer. + ** This Proc-Layer internal service is provided for use by the Player subsystem. + ** The actual implementation is forwarded to backend services (especially the scheduler). + ** The EngineService singleton has no state beyond the jobs currently managed by the + ** scheduler; when the latter isn't available, any invocation will throw. ** + ** The central concept provided through this facade interface is the calculation stream. + ** This represents a series of calculations, expected to happen in a timely fashion and in order + ** to deliver a frame data stream onto an opened output connection. On the implementation side, + ** a calculation stream will be translated into a series of jobs invoking render nodes, + ** to be executed through the scheduler in the backend layer. + ** + ** While the individual CalcStram is simple, linear and unmodifiable, any CalcStream may be + ** \em superseded by a new definition. In this case, the engine will care for a seamless + ** switch and continuation; under the hood, there is a mechanism to discard resources + ** tied to the original CalcStream, once the switch to the new definition is complete. + ** + ** @see EngineInterface_test + ** @see CalcStream_test ** @see proc::play::PlayerService */ @@ -46,6 +58,7 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" #include "lib/polymorphic-value.hpp" +#include "lib/singleton.hpp" // #include //#include @@ -82,18 +95,6 @@ namespace engine{ : boost::noncopyable { -// string error_; -// Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - -// typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) -// , DummyPlayer -// > ServiceInstanceHandle; - -// lib::SingletonRef implInstance_; -// ServiceInstanceHandle serviceInstance_; /* The following typedefs allow to hand out predefined * Quality-of-Service strategy definitions as value objects, @@ -117,8 +118,8 @@ namespace engine{ }; -// typedef lib::polyvalue::CloneValueSupport _ClonableQoS_Strategy; - typedef lib::PolymorphicValue QoS_Definition; + typedef lib::polyvalue::CloneValueSupport _Clonable_QoS_Strategy; + typedef lib::PolymorphicValue QoS_Definition; static QoS_Definition QoS_DEFAULT; static QoS_Definition QoS_BACKGROUND; @@ -127,10 +128,15 @@ namespace engine{ static QoS_Definition QoS_COMPROMISE; + /** access point to the Engine Interface. + * @internal this is an facade interface for internal use + * by the player. Client code should use the Player. + */ + static lib::Singleton instance; - EngineService(); /////TODO (Subsys::SigTerm terminationHandle); - ~EngineService() { } /////TODO notifyTermination_(&error_); } + EngineService(); + ~EngineService() { } CalcStream calculate(ModelPort mPort, @@ -143,6 +149,12 @@ namespace engine{ Timings nominalTimings, Quality serviceQuality =QoS_BACKGROUND); + + protected: + void activateTracing(); + void disableTracing(); ///< EX_FREE + + friend class EngineDiagnostics; }; diff --git a/tests/46engine.tests b/tests/46engine.tests index e669a11cb..28f68b6a6 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -7,6 +7,14 @@ return: 0 END +PLANNED "Engine Interface basics" EngineInterface_test < + + 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/test/run.hpp" +#include "lib/error.hpp" + +#include "proc/engine/engine-service.hpp" + +//#include +//#include +//#include + +using test::Test; +//using std::cout; +//using std::rand; + + +namespace proc { +namespace engine{ +namespace test { + + namespace { // test fixture... + + } // (End) test fixture + + + + /******************************************************************* + * @test detailed coverage of the various CalcStream flavours + * supported by the render engine interface. + * + * @todo WIP-WIP-WIP + * + * @see EngineInterface_test for the plain flat standard case + * @see EngineService + * @see CalcStream + * @see OutputSlotProtocol_test + */ + class CalcStream_test : public Test + { + + virtual void + run (Arg) + { + UNIMPLEMENTED ("in-depth coverage of calculation streams"); + } + + }; + + + /** Register this test class... */ + LAUNCHER (CalcStream_test, "function engine"); + + + +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/engine-interface-test.cpp b/tests/components/proc/engine/engine-interface-test.cpp new file mode 100644 index 000000000..be9ec028b --- /dev/null +++ b/tests/components/proc/engine/engine-interface-test.cpp @@ -0,0 +1,111 @@ +/* + EngineInterface(Test) - verify basics of the engine (scheduling) service + + Copyright (C) Lumiera.org + 2011, 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 "lib/test/run.hpp" +#include "lib/error.hpp" + +#include "proc/engine/calc-stream.hpp" +#include "proc/engine/engine-service.hpp" +#include "proc/engine/engine-diagnostics.hpp" +#include "proc/play/diagnostic-output-slot.hpp" +#include "proc/mobject/model-port.hpp" +#include "proc/asset/pipe.hpp" + +//#include +//#include +//#include + +using test::Test; +//using std::cout; +//using std::rand; + + +namespace proc { +namespace engine{ +namespace test { + + using asset::Pipe; + using asset::PPipe; + using mobject::ModelPort; + + typedef asset::ID PID; + + + namespace { // test fixture... + + } // (End) test fixture + + + + /******************************************************************* + * @test cover the basic service exposed at the engine interface: + * create a calculation stream and verify the translation + * into individual jobs. + * + * This test relies on the engine's diagnostic facilities, allowing + * to log and verify the generated jobs without needing to execute + * them. So this test doesn't actually run the engine. There are + * \link OutputSlotProtocol_test other tests \endlink covering + * the output generation separate from the engine. + * + * @see CalcStream_test more in-depth coverage of the various + * flavours of calculation streams supported by the engine + * @see EngineService + * @see CalcStream + * @see OutputSlotProtocol_test + */ + class EngineInterface_test : public Test + { + + virtual void + run (Arg) + { + UNIMPLEMENTED ("simple standard case of Engine interface usage"); + + EngineService& engine = EngineService::instance(); + EngineDiagnostics monitor(engine); + + PID pipe = Pipe::query("id(dummy)"); + ModelPort port(pipe); + + OutputSlot& oSlot = DiagnosticOutputSlot::build(); + Allocation output = oSlot.allocate(); + Timings timings; /////////TODO + + // Invoke test subject... + CalcStream calc = engine.calculate(port, timings, output); + + ////TODO some direct checks on the calculation stream?? + + CHECK (monitor.has_scheduled_jobs_for(timings)); + } + + }; + + + /** Register this test class... */ + LAUNCHER (EngineInterface_test, "function engine"); + + + +}}} // namespace proc::engine::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 49a1a6096..de9778782 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1916,7 +1916,7 @@ To support this usage pattern, the Fixture implementation makes use of the [[PIm * moreover, this necessitates a tight integration down to implementation level, both with the clean-up and the render processes themselves
-
+
The Fixture &rarr; [[data structure|FixtureDatastructure]] acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel).
 Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes (&rarr; CalcStream) are known to be terminated.
 
@@ -1934,7 +1934,7 @@ Basically the concern is that each new CalcStream had to access the shared count
 There are. As the builder is known to be run again and again, no one forces us to deallocate as soon as we could. That's the classical argument exploited by any garbage collector too. Thus we could just note the fact that a calculation stream is done and re-evaluate all those noted results on later occasion.
 
 !!exploiting the frame-dispatch step
-Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process / a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
+Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process (maybe even each CalcStram)  a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
 
 !!assessment {{red{WIP 12/10}}}
 But the primary question here is to judge the impact of such an implementation. What would be the costs?

From 7efde06569343c818945ad747b81864fb3f63487 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 18 Aug 2011 12:31:26 +0200
Subject: [PATCH 063/296] some stubs to make it compile...

---
 src/proc/engine/engine-diagnostics.hpp                 | 10 +++++++++-
 src/proc/engine/engine-service.cpp                     |  3 +--
 src/proc/play/diagnostic-output-slot.hpp               |  8 ++++++++
 tests/components/proc/engine/engine-interface-test.cpp |  6 +++++-
 4 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/src/proc/engine/engine-diagnostics.hpp b/src/proc/engine/engine-diagnostics.hpp
index bbb4052fb..be4e50859 100644
--- a/src/proc/engine/engine-diagnostics.hpp
+++ b/src/proc/engine/engine-diagnostics.hpp
@@ -43,7 +43,7 @@
 //#include "include/display-facade.h"
 //#include "proc/engine/calc-stream.hpp"
 //#include "proc/mobject/model-port.hpp"
-//#include "proc/play/timings.hpp"
+#include "proc/play/timings.hpp"
 //#include "proc/play/output-slot.hpp"
 //#include "common/instancehandle.hpp"
 //#include "lib/singleton-ref.hpp"
@@ -62,6 +62,7 @@ namespace engine{
 //    using lumiera::Subsys;
 //    using lumiera::Display;
 //    using lumiera::DummyPlayer;
+  using proc::play::Timings;
   
   
   
@@ -93,6 +94,13 @@ namespace engine{
           TODO ("detach tracing connector");
           engine_.disableTracing();
         }
+      
+      /** */
+      bool
+      has_scheduled_jobs_for (Timings const& timings)
+        {
+          UNIMPLEMENTED ("Engine Diagnostics: query scheduled jobs");
+        }
     };
   
   
diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp
index b85353211..e9d188a78 100644
--- a/src/proc/engine/engine-service.cpp
+++ b/src/proc/engine/engine-service.cpp
@@ -106,8 +106,7 @@ namespace engine{
   class PriorityQoS
     : public DefaultQoS
     {
-      CalcType type_;
-      
+    
     public:
       PriorityQoS ()
         : DefaultQoS(PLAYBACK)
diff --git a/src/proc/play/diagnostic-output-slot.hpp b/src/proc/play/diagnostic-output-slot.hpp
index e0e4a7f0a..b699e0ae8 100644
--- a/src/proc/play/diagnostic-output-slot.hpp
+++ b/src/proc/play/diagnostic-output-slot.hpp
@@ -62,6 +62,14 @@ namespace play {
     : public OutputSlot
     {
     public:
+      /** build a new Diagnostic Output Slot instance,
+       *  discard the existing one. Use the static query API
+       *  for investigating collected data. */
+      static OutputSlot&
+      build()
+        {
+          UNIMPLEMENTED ("Diagnostic Output Slot instance");
+        }
       
     private:
       
diff --git a/tests/components/proc/engine/engine-interface-test.cpp b/tests/components/proc/engine/engine-interface-test.cpp
index be9ec028b..fd55ee76f 100644
--- a/tests/components/proc/engine/engine-interface-test.cpp
+++ b/tests/components/proc/engine/engine-interface-test.cpp
@@ -27,6 +27,7 @@
 #include "proc/engine/calc-stream.hpp"
 #include "proc/engine/engine-service.hpp"
 #include "proc/engine/engine-diagnostics.hpp"
+#include "proc/play/output-slot.hpp"
 #include "proc/play/diagnostic-output-slot.hpp"
 #include "proc/mobject/model-port.hpp"
 #include "proc/asset/pipe.hpp"
@@ -47,8 +48,11 @@ namespace test  {
   using asset::Pipe;
   using asset::PPipe;
   using mobject::ModelPort;
+  using proc::play::OutputSlot;
+  using proc::play::DiagnosticOutputSlot;
   
   typedef asset::ID PID;
+  typedef OutputSlot::Allocation Allocation;
 
   
   namespace { // test fixture...
@@ -84,7 +88,7 @@ namespace test  {
           
           EngineService& engine = EngineService::instance();
           EngineDiagnostics monitor(engine);
-
+          
           PID pipe = Pipe::query("id(dummy)");
           ModelPort port(pipe);
           

From d1a5b9a9143bc354ec748a1c970ad7fa33184e8e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 24 Aug 2011 01:26:36 +0200
Subject: [PATCH 064/296] main: temporarily disable dependencies...

disable dependency declarations for subsystems
not yet implemented, to allow the lumiera executable
to start up
---
 src/lumiera/main.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp
index f738337bf..69b5ecc22 100644
--- a/src/lumiera/main.cpp
+++ b/src/lumiera/main.cpp
@@ -63,11 +63,11 @@ main (int argc, const char* argv[])
       lumiera::Option options (args);
       application.init (options);
       
-      session.depends (builder);
+//    session.depends (builder);
       netNode.depends (session);
       netNode.depends (engine);
-      playOut.depends (engine);
-      playOut.depends (session);
+//    playOut.depends (engine);
+//    playOut.depends (session);
 //    lumigui.depends (session);   //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin
 //    lumigui.depends (engine);
       player.depends (playOut);    //////TODO dummy player, until we're able to render

From 0706b83a46dc78895d79bbe8086e8fe6054e31bd Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 25 Aug 2011 15:08:27 +0200
Subject: [PATCH 065/296] fix a warning

---
 src/lib/lifecycle.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/lib/lifecycle.cpp b/src/lib/lifecycle.cpp
index 1894ae2fb..24bbf04ae 100644
--- a/src/lib/lifecycle.cpp
+++ b/src/lib/lifecycle.cpp
@@ -76,11 +76,11 @@ namespace lumiera {
 extern "C" { /* ==== implementation C interface for lifecycle hooks ======= */
   
   
-  extern const char * lumiera_ON_BASIC_INIT       = lumiera::ON_BASIC_INIT;
-  extern const char * lumiera_ON_GLOBAL_INIT      = lumiera::ON_GLOBAL_INIT;
-  extern const char * lumiera_ON_GLOBAL_SHUTDOWN  = lumiera::ON_GLOBAL_SHUTDOWN;
+  const char * lumiera_ON_BASIC_INIT       = lumiera::ON_BASIC_INIT;
+  const char * lumiera_ON_GLOBAL_INIT      = lumiera::ON_GLOBAL_INIT;
+  const char * lumiera_ON_GLOBAL_SHUTDOWN  = lumiera::ON_GLOBAL_SHUTDOWN;
   
-  extern const char * lumiera_ON_EMERGENCY        = lumiera::ON_EMERGENCY;
+  const char * lumiera_ON_EMERGENCY        = lumiera::ON_EMERGENCY;
   
   
   

From 95bb5e64aa01393d05ad5479842f581efe079a2b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 4 Sep 2011 01:54:36 +0200
Subject: [PATCH 066/296] WIP desiging the interplay of BufferProvider and
 BuffHandle

---
 src/gui/display-service.hpp             |   1 +
 src/proc/engine/buffer-provider.cpp     |  35 ++++++-
 src/proc/engine/buffer-provider.hpp     |  14 ++-
 src/proc/engine/buffhandle.hpp          | 118 +++++++++++++-----------
 src/proc/engine/nodeinvocation.hpp      |   2 +-
 src/proc/mobject/output-designation.hpp |   2 +
 wiki/renderengine.html                  |  60 +++++++++---
 7 files changed, 162 insertions(+), 70 deletions(-)

diff --git a/src/gui/display-service.hpp b/src/gui/display-service.hpp
index 1dec2c192..1c5cda79f 100644
--- a/src/gui/display-service.hpp
+++ b/src/gui/display-service.hpp
@@ -50,6 +50,7 @@
 #include "common/instancehandle.hpp"
 #include "lib/singleton-ref.hpp"
 #include "lib/scoped-ptrvect.hpp"
+#include "include/logging.h"
 
 #include 
 #include 
diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index e58168c9a..a3d0bc51b 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -24,12 +24,45 @@
 #include "proc/engine/buffer-provider.hpp"
 
 namespace engine {
+  
+  
+  namespace { // impl. details and definitions
+    
+    const uint DEFAULT_DESCRIPTOR = 0;
+  }
 
 
   BufferProvider::~BufferProvider() { }
   
   
-  /**  */
+  /** @internal verify the given descriptor.
+   *  @return true if it corresponds to a buffer
+   *          currently locked and usable by client code
+   */
+  bool
+  BufferProvider::checkValidity (BufferDescriptor const&)
+  {
+    UNIMPLEMENTED ("BufferProvider basic and default implementation");
+  }
+  
+  
+  BufferDescriptor
+  BufferProvider::getDefaultDescriptor()
+  {
+    return BufferDescriptor (*this, DEFAULT_DESCRIPTOR);
+  }
+      
+      
+  
+  
+  /* === BufferDescriptor and BuffHandle === */
+  
+  bool
+  BufferDescriptor::checkValidity()
+  {
+    return provider_.checkValidity(*this);
+  }
+  
 
 
 } // namespace engine
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index 6b6429c21..7f8f4f99b 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -24,7 +24,7 @@
  ** Abstraction to represent buffer management and lifecycle within the render engine.
  ** It turns out that --  throughout the render engine implementation -- we never need
  ** direct access to the buffers holding media data. Buffers are just some entity to be \em managed,
- ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementatino detail.
+ ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementation detail.
  ** The code within the render engine just pushes around BufferHandle objects, which act as a front-end,
  ** being created by and linked to a BufferProvider implementation. There is no need to manage the lifecycle
  ** of buffers automatically, because the use of buffers is embedded into the render calculation cycle,
@@ -73,10 +73,18 @@ namespace engine {
     public:
       virtual ~BufferProvider();  ///< this is an interface
       
-      ///////TODO: is there any generic way to obtain a BufferDescriptor; then we should expose it here...
       
       virtual BuffHandle lockBufferFor (BufferDescriptor const&)  =0;
-      virtual void releaseBuffer (BuffHandle const&)              =0;  ////////TODO not quite sure what information to pass here
+      virtual void releaseBuffer (BuffHandle const&)              =0;
+      
+      
+      /** describe the kind of buffer managed by this provider */
+      BufferDescriptor getDefaultDescriptor();
+      
+      
+      /* === API for BuffHandle internal access === */
+      
+      bool checkValidity (BufferDescriptor const&);
       
     };
   
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index d7c5ecd87..60992c058 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -52,71 +52,37 @@
 
 namespace engine {
   
+  class BufferProvider;
+  
   
   
   /**
-   * Handle for a buffer for processing data, abstracting away the actual implementation.
-   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
+   * An opaque descriptor to identify the type and further properties of a data buffer.
+   * For each kind of buffer, there is somewhere a BufferProvider responsible for the
+   * actual storage management. This provider may "lock" a buffer for actual use,
+   * returning a BuffHandle.
    * 
-   * @todo as of 6/2011 it isn't clear how buffer handles are actually created
-   *       and how the lifecycle (and memory) management works
+   * @todo try to move that definition into buffer-provider.hpp   ////////////////////////////////////TICKET #249
    */
-  struct BuffHandle
-    : lib::BoolCheckable
+  class BufferDescriptor
     {
-      typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
-      typedef Buff* PBuff;
+      BufferProvider* provider_;
+      uint64_t subClassification_;
       
-      PBuff 
-      operator->()  const 
-        { 
-          return pBuffer_; 
-        }
-      Buff&
-      operator* ()  const
-        {
-          ENSURE (pBuffer_);
-          return *pBuffer_;
-        }
+      BufferDescriptor(BufferProvider& manager, uint64_t detail)
+        : provider_(&manager)
+        , subClassification_(detail)
+      { }
       
-      bool
-      isValid()  const
-        {
-          return pBuffer_;
-        }
+      friend class BufferProvider;
       
+    public:
+      // using standard copy operations
       
-      //////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> TICKET #249
-      BuffHandle()
-        : pBuffer_(0),
-          sourceID_(0)
-        { }
-      
-      /** 
-       * @deprecated placeholder implementation
-       * @todo rework the Lifecycle handling of buffers  //////////TICKET #249
-       */
-      BuffHandle(PBuff existingBuffer)
-        : pBuffer_(existingBuffer)
-        , sourceID_(0)
-        { }
-      
-    private:
-      PBuff pBuffer_; 
-      long sourceID_;   ////TICKET #249 this is a placeholder for a "type-like information", to be used for lifecycle management and sanity checks....
+      bool checkValidity();
     };
   
   
-  /**
-   * Buffer Type information.
-   * Given a BufferDescriptor, it is possible to allocate a buffer
-   * of suitable size and type by using BufferProvider::lockBuffer().
-   */
-  struct BufferDescriptor
-    {
-      long typeToken_; 
-    };
-  
   
   class ProcNode;
   typedef ProcNode* PNode;
@@ -135,5 +101,53 @@ namespace engine {
   
   
   
+  
+  
+  /**
+   * Handle for a buffer for processing data, abstracting away the actual implementation.
+   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
+   * 
+   * @todo as of 6/2011 it isn't clear how buffer handles are actually created
+   *       and how the lifecycle (and memory) management works                  //////////////////////TICKET #249 rework BuffHandle creation and usage
+   */
+  class BuffHandle
+    : public lib::BoolCheckable
+    {
+      typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
+      
+      BufferDescriptor descriptor_;
+      Buff* pBuffer_; 
+      
+      
+    public:
+      /** @internal a buffer handle may be obtained by "locking"
+       *  a buffer from the corresponding BufferProvider */
+      BuffHandle(BufferDescriptor const& typeInfo, Buff* storage = 0)
+        : descriptor_(typeInfo)
+        , pBuffer_(storage)
+        { }
+      
+      // using standard copy operations
+      
+      
+      Buff&
+      operator* ()  const
+        {
+          ENSURE (pBuffer_);
+          return *pBuffer_;
+        }
+      
+      bool
+      isValid()  const
+        {
+          return bool(pBuffer_)
+              && descriptor_.checkValidity();
+        }
+      
+    };
+  
+  
+  
+  
 } // namespace engine
 #endif
diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp
index ad9c6d402..1a7575297 100644
--- a/src/proc/engine/nodeinvocation.hpp
+++ b/src/proc/engine/nodeinvocation.hpp
@@ -172,7 +172,7 @@ namespace engine {
     };
     
     
-                                                                                                    ////////////TICKET #249  this strategy should better be hidden within the BuffHanle ctor (and type-erased after creation)
+                                                                                                    ////////////TICKET #249  this strategy should better be hidden within the BuffHandle ctor (and type-erased after creation)
     struct AllocBufferFromParent  ///< using the parent StateAdapter for buffer allocations
       : Invocation
       {
diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp
index e5d32813d..93f98f518 100644
--- a/src/proc/mobject/output-designation.hpp
+++ b/src/proc/mobject/output-designation.hpp
@@ -59,6 +59,8 @@ namespace mobject {
    * @todo couldn't the inline buffer be "downgraded" to InPlaceBuffer or PolymorphicValue??
    *       Seemingly we never-ever need to re-discover the erased type of the embedded spec.
    *       Thus for this to work, we'd just need to add an "empty" spec       ///////////////////TICKET #723
+   * 
+   * @see OutputMapping_test
    */
   class OutputDesignation
     {
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index de9778782..8cae9bde7 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1077,12 +1077,45 @@ Please note the shortcomings and logical contradictions in the solution currentl
 * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
-
-
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''buffer handles'', which act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
+
+
All rendering, transformations and output of media data requires using ''data buffers'' -- but the actual layout and handling of these buffers is closely related to the actual implementation of these operations. As we're relying heavily on external libraries and plug-ins for performing these, there is no hope getting away with just one single {{{Buffer}}} data type definition. Thus, we need to confine ourselves to a common denominator of basic operations regarding data buffers and abstract the access to these operations through a BufferProvider entity. Beyond these basic operations, mostly we just need to assure that //a buffer exists as an distinguishable element// -- which in practice boils down to pushing around {{{void*}}} variables. 
+
+Obviously, overloading a pointer with semantic meaning isn't exactly a brilliant idea -- and the usual answer is to embed this pointer into a smart handle, which also yields the nice side-effect of explaining this design to the reader. Thus a buffer handle
+* can only be obtained from a BufferProvider
+* can be used to identify a buffer
+* can be dereferenced
+* can be copied 
+
+!design quest: buffer type information
+To perform anything useful with such a buffer handle, the client code needs some additional information, which can be generalised into a //type information:// Either, the client needs to know the size and kind of data to expect in the buffer, maybe just assume to get a specific buffer with suitably dimensions, or the client needs to know which buffer provider to contact for any management operations on that buffer (handle). And, at some point there needs to be a mechanism to verify the validity of a handle. But all of this doesn't mean that it's necessary to encode or embedd this information directly into the handle -- it might also be stored into a registration table (which has the downside of creating contention), or it might just be attached implicitly to the invocation context.
+
+Just linking this type information to the context is certainly the most elegant solution, but also by far the most difficult to achieve -- not to mention the implicit dependency on a very specific invocation situation. So for now (9/2011) it seems best to stick to the simple and explicit implementation, just keeping that structural optimisation in mind. And the link to this buffer type information should be made explicit within the definition anyway, even if we choose to employ another design tradeoff later.
+* thus the conclusion is: we introduce a ''descriptor object'', which will be stored within the handle
+* each BufferProvider exposes a ''descriptor prototype''; it can be specialised and used by to organise implementation details
+
+
+!sanity checks
+there are only limited sanity checks, and they can be expected to be optimised away for production builds.
+Basically the client is responsible for sane buffer access.
+
+
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
 * that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
 * that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
 * that buffers are marked as free ("released") after doing the actual calculations.
 
+!operations
+While BufferProvider is an interface meant to be backed by various different kinds of buffer and memory management approaches, there is a common set of operations to be supported by any of them
+;announcing
+:client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations (or similar mechanisms to ensure the avialability) to happen right away; the BufferProvider will then return the actual number of buffers guraanteed to be available. This announcing step is optional an can happen anytime before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. (Currently 9/2011 this is meant to be global for the whole BufferProvider, but it might happen that we need to break that down to individual clients)
+;locking
+:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again until released. If necessary, the BufferProvider might at that point allocate memory to accomodate (especially when the buffers weren't announced beforehand). The locking might fail and raise an exception. To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
+;attaching
+:optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be automatically destroyed when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, which will be invoked in a similar manner. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
+;releasing
+:buffers need to be released explicitly by the client code. This renders the corresponding BuffHandle invalid, (optionally) invokes a destructor function of an attached object and maybe reclaims the buffer memory
+
 __see also__
 &rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
 &rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
@@ -1853,8 +1886,9 @@ Some further details
 * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
 
-
-
a specially configured view -- joining together high-level and low-level model
+
+
a specially configured view -- joining together high-level and low-level model.
+The Fixture acts as //isolation layer// between the two models, and as //backbone to attach the render nodes.//
 * all MObjects have their position, length and configuration set up ready for rendering.
 * any nested sequences (or other kinds of indirections) have been resolved.
 * every MObject is attached by an ExplicitPlacement, which declares a fixed position (Time, [[Pipe|OutputDesignation]])
@@ -1886,8 +1920,8 @@ The fixture is like a grid, where one dimension is given by the [[model ports|Mo
 :Thus the exit nodes are keyed by ~Pipe-ID as well (and consequently have a distinct [[stream type|StreamType]]) -- each model port coresponds to {{{<number_of_segments>}}} separate exit nodes, but of course an exit node may be //mute.//
 
-
-
Generally speaking, the [[here|Fixture]] comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline.
+
+
Generally speaking, the datastructure to implement the ''Fixture'' (&rarr; see a more general description [[here|Fixture]]) is comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline.
 This page focusses on the actual data structure and usage details on that level. See also &rarr; [[storage|FixtureStorage]] considerations.
 
 !transactional switch
@@ -3238,7 +3272,7 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
 As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3255,19 +3289,19 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created using a [[Pipe]]-ID and &mdash; they become real by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of [[model ports|ModelPort]] as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
+Initially, [[Output designations|OutputDesignation]] are typically just local or relative references to another OutputDesignation; yet after some resolution steps, we'll arrive at an OutputDesignation //defined absolute.// Basically, these are equivalent to a [[Pipe]]-ID choosen as target for the connection and -- they become //real//&nbsp; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of [[model ports|ModelPort]] as part of the current [[Fixture]]. A RenderProcess can be started to pull from these -- and only from these -- active exit points of the model. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping just relies on relative addressing of the output sinks, starting to allocate connections with the //first of each kind// (e.g. "connect to the first usable audio output destination").
 
-We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
+We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven and constrained by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format -- again with fixed and pre-settled channel configuration ({{red{TODO 9/11}}} when configurting a render process, it might be necessary to pre-compute the //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
 !Connection to external outputs
-External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
+External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read: actually there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
 Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
 &rarr; the OutputManager interface describes handling this mapping association
 &rarr; see also the PlayService
 
 !the global output manager
-While within the model routing is done mostly just by referring to an OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
+While within the model routing is done mostly just by referring to an OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes these mappings and allows to control and manage them. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
 
 The relation between the central OutputDirector and the peripheral OutputManager implementations is hierarchical. Because output slots are usually registered rather at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. Resolving a global pipe into an output slot is the core concern of any OutputManager implementation. Thus, when there is a locally preconfigured mapping, like e.g. for a viewer's video master pipe to the output slot installed by the corresponding GUI viewer element, then this mapping will picked up foremost to resolve the video master output.
 
@@ -3324,7 +3358,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3372,7 +3406,7 @@ If we accept to retrieve the buffer(s) via an indirection, which we kind of do a
 &rArr; conclusion: go for the unified approach!
 
 !!!unified data exchange cycle
-The nominal time of a frame to be delivered is used as an ID throughout that cycle
+The planned delivery time of a frame is used as an ID throughout that cycle
 # within a defined time window prior to delivery, the client can ''allocate and retrieve the buffer'' from the BufferProvider.
 # the client has to ''emit'' within a (short) time window prior to deadline
 # now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.

From 8016547d9c8a3ec4ec07e362c5309010b89054c6 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 16 Sep 2011 01:58:13 +0200
Subject: [PATCH 067/296] rework and clarify node invocation sequence

while passing by, identified quite some
node invocation code to be rewritten
---
 src/proc/engine/buffer-provider.cpp |  4 +-
 src/proc/engine/buffhandle.hpp      | 10 ++--
 src/proc/engine/bufftable.hpp       | 22 ++++++-
 src/proc/engine/nodeinvocation.hpp  |  4 +-
 src/proc/engine/stateproxy.cpp      |  2 +-
 src/proc/engine/stateproxy.hpp      |  2 +-
 src/proc/state.hpp                  |  3 +-
 wiki/renderengine.html              | 91 ++++++++++++++++++++---------
 8 files changed, 99 insertions(+), 39 deletions(-)

diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index a3d0bc51b..57660a572 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -58,9 +58,9 @@ namespace engine {
   /* === BufferDescriptor and BuffHandle === */
   
   bool
-  BufferDescriptor::checkValidity()
+  BufferDescriptor::checkValidity()  const
   {
-    return provider_.checkValidity(*this);
+    return provider_->checkValidity(*this);
   }
   
 
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 60992c058..835d0329c 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -79,7 +79,7 @@ namespace engine {
     public:
       // using standard copy operations
       
-      bool checkValidity();
+      bool checkValidity()  const;
     };
   
   
@@ -88,9 +88,9 @@ namespace engine {
   typedef ProcNode* PNode;
   
   
-  struct ChannelDescriptor  ///////TODO collapse this with BufferDescriptor?
+  struct ChannelDescriptor  ///////TODO really need to define that here? it is needed for node wiring only
     {
-      BufferDescriptor bufferType;
+      const lumiera::StreamType * bufferType;                /////////////////////////////////////////TICKET #828
     };
   
   struct InChanDescriptor : ChannelDescriptor
@@ -120,9 +120,11 @@ namespace engine {
       
       
     public:
+      typedef Buff* PBuff;
+      
       /** @internal a buffer handle may be obtained by "locking"
        *  a buffer from the corresponding BufferProvider */
-      BuffHandle(BufferDescriptor const& typeInfo, Buff* storage = 0)
+      BuffHandle(BufferDescriptor const& typeInfo, PBuff storage = 0)
         : descriptor_(typeInfo)
         , pBuffer_(storage)
         { }
diff --git a/src/proc/engine/bufftable.hpp b/src/proc/engine/bufftable.hpp
index 3682b51ff..ac5773e24 100644
--- a/src/proc/engine/bufftable.hpp
+++ b/src/proc/engine/bufftable.hpp
@@ -48,6 +48,10 @@ namespace engine {
      * data buffers. The tables are supposed to be implemented as bare "C" arrays,
      * thus the array of real buffer pointers can be fed directly to the
      * processing function of the respective node.
+     * 
+     * @todo this whole design is a first attempt and rather clumsy. It should be reworked
+     *       to use a single contiguous memory area and just layer the object structure on top
+     *       (by using placement new). Yet the idea of an stack-like organisation should be retained
      */
   struct BuffTable
     {
@@ -64,7 +68,21 @@ namespace engine {
   
   class BuffTableStorage
     {
-      vector        hTab_;
+      /////////////////////////////////////////////////////////////////////////TICKET #826  need to be reworked entirely
+      /** just a placeholder to decouple the existing code
+       *  from the reworked BuffHandle logic. The existing
+       *  code in turn will be reworked rather fundamentally
+       */
+      struct BuffHaXXXX
+        : BuffHandle
+        {
+          BuffHaXXXX() : BuffHandle(just_satisfy_the_compiler()) { /* wont work ever */ }
+          static BufferDescriptor const& 
+          just_satisfy_the_compiler() { }
+        };
+        
+                                           ////////////////////////////////////TICKET #825  should be backed by mpool and integrated with node invocation
+      vector        hTab_;
       vector pTab_;
       size_t level_;
       
@@ -139,7 +157,7 @@ namespace engine {
         {
           const uint nrO(wd.nrO);
           
-          // Setup the public visible table locations
+          // Setup the publicly visible table locations
           this->outHandle = &tab_.first[ 0 ];
           this->inHandle  = &tab_.first[nrO];
           this->outBuff   = &tab_.second[ 0 ];
diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp
index 1a7575297..695406cda 100644
--- a/src/proc/engine/nodeinvocation.hpp
+++ b/src/proc/engine/nodeinvocation.hpp
@@ -180,7 +180,7 @@ namespace engine {
           : Invocation(sta, w, outCh) {}
         
         virtual BuffHandle
-        allocateBuffer (BufferDescriptor const& bd) { return parent_.allocateBuffer(bd); }          ////////////TODO: actually implement the "allocate from parent" logic!
+        allocateBuffer (const lumiera::StreamType* ty) { return parent_.allocateBuffer(ty); }          ////////////TODO: actually implement the "allocate from parent" logic!
       };
     
     struct AllocBufferFromCache   ///< using the global current State, which will delegate to Cache
@@ -190,7 +190,7 @@ namespace engine {
           : Invocation(sta, w, outCh) {}
         
         virtual BuffHandle
-        allocateBuffer (BufferDescriptor const& bd) { return current_.allocateBuffer(bd); }
+        allocateBuffer (const lumiera::StreamType* ty) { return current_.allocateBuffer(ty); }
       };
     
   
diff --git a/src/proc/engine/stateproxy.cpp b/src/proc/engine/stateproxy.cpp
index cb32f3b57..90e36a094 100644
--- a/src/proc/engine/stateproxy.cpp
+++ b/src/proc/engine/stateproxy.cpp
@@ -38,7 +38,7 @@ namespace engine {
   
   /** @internal */
   BuffHandle
-  StateProxy::allocateBuffer (BufferDescriptor const&)
+  StateProxy::allocateBuffer (const lumiera::StreamType*)
   {
     UNIMPLEMENTED ("allocate a suitable buffer to hold a frame of the denoted type");
   }
diff --git a/src/proc/engine/stateproxy.hpp b/src/proc/engine/stateproxy.hpp
index 7125790f7..bca680be3 100644
--- a/src/proc/engine/stateproxy.hpp
+++ b/src/proc/engine/stateproxy.hpp
@@ -38,7 +38,7 @@ namespace engine {
       
     private: /* === top-level implementation of the State interface === */
       
-      BuffHandle allocateBuffer (BufferDescriptor const&);
+      BuffHandle allocateBuffer (const lumiera::StreamType*);              //////////////////////////TICKET #828
       
       void releaseBuffer (BuffHandle& bh);
       
diff --git a/src/proc/state.hpp b/src/proc/state.hpp
index ca0a6a44c..f0e863aaa 100644
--- a/src/proc/state.hpp
+++ b/src/proc/state.hpp
@@ -54,6 +54,7 @@ namespace engine {
    */
   class State
     {
+                                   ////////////////////////////////////////////////TICKET #826  expected to be reworked to quite some extent (9/2011)
     public:
       /** allocate a new writable buffer with type and size according to
        *  the BufferDescriptor. The actual provider of this buffer depends
@@ -62,7 +63,7 @@ namespace engine {
        *  @return a BuffHandle encapsulating the information necessary to get
        *          at the actual buffer address and for releasing the buffer.
        */
-      virtual BuffHandle allocateBuffer (BufferDescriptor const&)  =0;
+      virtual BuffHandle allocateBuffer (const lumiera::StreamType*)  =0;
       
       /** resign control of the buffer denoted by the handle */
       virtual void releaseBuffer (BuffHandle&)  =0;
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 8cae9bde7..6cff8960f 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1099,7 +1099,29 @@ there are only limited sanity checks, and they can be expected to be optimised a
 Basically the client is responsible for sane buffer access.
 
-
+
+
Buffers are used to hold the media data for processing and output. Within the Lumiera RenderEngine and [[Player]] subsystem, we use some common concepts to handle the access and allocation of working buffers. Yet this doesn't imply having only one central authority in charge of every buffer -- such an approach wouldn't be possible (due to collaboration with external systems) and wouldn't be desirable either. Rather, there are some common basic usage //patterns// -- and there are some core interfaces used throughout the organisation of the rendering process.
+
+Mostly, the //client code,// i.e. code in need of using buffers, can access some BufferProvider, thereby delegating the actual buffer management. This binds the client to adhere to kind of a //buffer access protocol,// comprised of the ''announcing'', ''locking'', optionally ''attaching'' and finally the ''releasing'' steps. Here, the actual buffer management within the provider is a question of implementation and will be configured during build-up of the scope in question.
+
+!usage situations
+;rendering
+:any calculations and transformations of media data typically require an input- and output buffer. To a large extent, these operations will be performed by specialised libraries, resulting in a call to some plain-C function receiving pointers to the required working buffers. Our invocation code has the liability to prepare and provide those pointers, relying on a BufferProvider in turn.
+;output
+:most any of the existing libraries for handling external output require the client to adhere to some specific protocol. Often, this involves some kind of callback invoked at the external library's discretion, thus forcing our engine to prepare data within an intermediary buffer. Alternatively, the output system might provide some mechanism to gain limited direct access to the output buffers, and such an access can again be exposed to our internal client code through the BufferProvider abstraction.
+
+!primary implementations
+;memory pool
+:in all those situations, where we just need a working buffer for some time, we can rely on our internal custom memory allocator.
+:{{red{~Not-Yet-Implemented as of 9/11}}} -- as a fallback we just rely on heap allocations through the language runtime
+;frame cache
+:whenever a calculated result may be of further interest beyond the immediate need triggering the calculation, it might be eligible for caching.
+:The Lumiera ''frame cache'' is a special BufferProvider, maintaining a larger pool of buffers which can be pinned and kept around for some time,
+:accomodating limited resources and current demand for fresh result buffers.
+
+
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
 * that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
 * that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
@@ -1118,6 +1140,7 @@ While BufferProvider is an interface meant to be backed by various different kin
 
 __see also__
 &rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
+&rarr; more about BufferManagement within the RenderEngine and [[Player]] subsystem
 &rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
 
@@ -3143,16 +3166,27 @@ While the general approach and reasoning remains valid, a lot of the details loo In the most general case the render network may be just a DAG (not just a tree). Especially, multiple exit points may lead down to the same node, and following each of this possible paths the node may be at a different depth on each. This rules out a simple counter starting from the exit level, leaving us with the possibility of either employing a rather convoluted addressing scheme or using arbitrary ID numbers.{{red{...which is what we do for now}}}
-
-
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. There is no such thing as an "engine object" calling nodes iteratively or table driven, rather, the nodes themselves issue recursive calls to their predecessor(s). For this to work, we need the nodes to adhere to a specific protocol:
-# Node is pulled, with a StateProxy object as parameter (encapsulating the access to the frames or buffers)
-# Node may now access current parameter values, using the state accessible via the StateProxy
-# using it's //input-output and wiring descriptor,// the Node creates a StateAdapter wrapping the StateProxy for allocating buffers and accessing the required input
-# StateAdapter might first try to get the output frames from the Cache in the Backend. In case of failure, a {{{process()}}} call is prepared by generating {{{pull()}}} call(s) for the input
-# as late as possible, typically on return, these recursive pull-calls have allocated a buffer containing the input data.
-# when input is ready prior to the {{{process()}}} call, output buffers will be allocated, either from the cache, or (if not caching) from the "parent" StateAdapter up the callstack.
-# after all buffers are available, the StateAdapter issues the {{{process()}}} call back to the originating node, which now may dereference the frame pointers and do its calculations
-# finally, when the {{{pull()}}} call returns, "parent" state originating the pull holds onto the buffers containing the calculated output result.
+
+
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. Meaning that there isn't an central entity invoking nodes consecutively. Rather, the nodes themselves contain the detailed knowledg regarding prerequisites, so the calculation plan is worked out recursively. Yet there are some prerequisite resources to be made available for any calculation to happen. Thus the actual calculation is broken down into atomic chunks of work, resulting in a 2-phase invocation whenever "pulling" a node. For this to work, we need the nodes to adhere to a specific protocol:
+;planning phase
+:when a node invocation is foreseeable to be required for getting a specific frame for a specific nominal and actual time, the engine has to find out the actual operations to happen
+:# the planning is initiated by issuing an "get me output" request, finally resulting in a JobTicket
+:# recursively, the node propagates "get me output" requests for its prerequisites
+:# after retrieving the planning information for these prerequisites, the node encodes specifics of the actual invocation situation into a closure called StateAdapter <br/>{{red{TODO: why not just labeling this &raquo;~StateClosure&laquo;?}}}
+:# finally, all this information is packaged into a JobTicket representing the planning results.
+;pull phase
+:now the actual node invocation is embedded within a job, activated through the scheduler to deliver //just in time.//
+:# Node is pulled, with a StateProxy object as parameter (encapsulating BufferProvider for access to the required frames or buffers)
+:# Node may now retrieve current parameter values, using the state accessible via the StateProxy
+:# to prepare for the actual {{{process()}}} call, the node now has to retrieve the input prerequisites
+:#* when the planning phase determined availability from the cache, then just these cached buffer(s) are now retrieved, dereferencing a BuffHandle
+:#* alternatively the planning might have arranged for some other kind of input to be provided through a prerequisite Job. Again, the corresponding BuffHandle can now be dereferenced
+:#* Nodes may be planned to have a nested structure, thus directly invoking {{{pull()}}} call(s) to prerequisite nodes without further scheduling
+:# when input is ready prior to the {{{process()}}} call, output buffers will be allocated by locking the output [[buffer handles|BuffHandle]] prepared during the planning phase
+:# since all buffers and prerequistes are available, the Node may now prepare a frame pointer array and finally invoke the external {{{process()}}} to kick off the actual calculations
+:# finally, when the {{{pull()}}} call returns, "parent" state originating the pull holds onto the buffers containing the calculated output result.
+{{red{WIP as of 9/11  -- many details here are still to be worked out and might change as we go}}}
+
 some points to note:
 * the WiringDescriptor is {{{const}}} and precalculated while building (remember another thread may call in parallel)
 * when a node is "inplace-capable", input and output buffer may actually point to the same location
@@ -3469,7 +3503,7 @@ There is rather strong separation between these two levels, and &mdash; <
 [img[Block Diagram|uml/fig128005.png]]
 
-
+
Render Engine, [[Builder]] and [[Controller]] are closely related Subsystems. Actually, the [[Builder]] //creates// a newly configured Render Engine //for every// RenderProcess. Before doing so, it queries from the Session (or, to be more precise, from the [[Fixture]] within the current Session) all necessary Media Object Placement information. The [[Builder]] then derives from this information the actual assembly of [[Processing Nodes|ProcNode]] comprising the Render Engine. Thus:
  * the source of the build process is a sequence of absolute (explicit) [[Placements|Placement]] called the [[Playlist]]
  * the [[build process|BuildProcess]] is driven, configured and controlled by the [[Controller]] subsystem component. It encompasses the actual playback configuration and State of the System.
@@ -3477,6 +3511,7 @@ There is rather strong separation between these two levels, and &mdash; <
 
 see also: RenderEntities, [[two Examples (Object diagrams)|Examples]] 
 
+{{red{TODO: adjust terminology in this drawing: "Playlist" &rarr; "Fixture" and "Graph" &rarr; "Segment"}}}
 [img[Overview: Components of the Renderengine|uml/fig128261.png]]
 
@@ -4961,23 +4996,26 @@ At first sight the link between asset and clip-MO is a simple logical relation b &rarr; EngineFaçade
-
+
The [[Render Engine|Rendering]] only carries out the low-level and performance critical tasks. All configuration and decision concerns are to be handled by [[Builder]] and [[Controller]]. While the actual connection of the Render Nodes can be highly complex, basically each Segment of the Timeline with uniform characteristics is handled by one Processor, which is a graph of [[Processing Nodes|ProcNode]] discharging into a ExitNode. The Render Engine Components as such are //stateless// themselves; for the actual calculations they are combined with a StateProxy object generated by and connected internally to the [[Controller]], while at the same time holding the Data Buffers (Frames) for the actual calculations.
 
+{{red{Warning: obsolete as of 9/11}}}
+Currently the Render/Playback is beeing targetted for implementation; almost everything in this diagram will be implemented in a slightly differently way....
 [img[Entities comprising the Render Engine|uml/fig128389.png]]
 
-
+
Below are some notes regarding details of the actual implementation of the render process and processing node operation. In the description of the [[render node operation protocol|NodeOperationProtocol]] and the [[mechanics of the render process|RenderMechanics]], these details were left out deliberately.
+{{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
 !Layered structure of State
 State can be seen as structured like an onion. All the [[StateAdapter]]s in one call stack are supposed to be within one layer: they all know of a "current state", which in turn is a StateProxy (and thus may refer yet to another state, maybe accros the network or in the backend or whatever). The actual {{{process()}}} function "within" the individual nodes just sees a single StateAdapter and thus can be thought to be a layer below.
 
 !Buffer identification
-For the purpose of node operation, Buffers are identified by a //Buffer-handle,// which contains both the actual buffer pointer and an internal indes and classification of the source providing the buffer; the latter information is used for deallocation. Especially for calling the {{{process()}}} function (which is supposed to be plain C) the respective StateAdapter provides an array containing just the output and input buffer pointers
+For the purpose of node operation, Buffers are identified by a [[buffer-handle|BuffHandle]], which contains both the actual buffer pointer and an internal indes and classification of the source providing the buffer; the latter information is used for deallocation. Especially for calling the {{{process()}}} function (which is supposed to be plain C) the node invocation needs to prepare and provide an array containing just the output and input buffer pointers. Typically, this //frame pointer array//&nbsp; is allocated on the call stack.
 
 !Problem of multi-channel nodes
-Some data processors simply require to work on multiple channels simultanously, while others work just on a single channel and will be replicated by the builder for each channel invoved. Thus, we are struck with the nasty situation that the node graph may go through some nodes spanning the chain of several channels. Now the decision is //not to care for this complexity within a single chain calculating a single channel.// We rely solely on the cache to avoid duplicated calculations. When a given node happens to produce multiple output buffers, we are bound to allocate them for the purpose of this nodes {{{process()}}} call, but we just "let go" the buffers not needed immediately for the channel acutally to be processed. For this to work, it is supposed that the builder has wired in a caching, and that the cache will hit when we touch the same node again for the other channels.
+Some data processors simply require to work on multiple channels simultanously, while others work just on a single channel and will be replicated by the builder for each channel invoved. Thus, we are struck with the nasty situation that the node graph may go through some nodes spanning the chain of several channels. Now the decision is //not to care for this complexity within a single chain calculating a single channel.// We rely solely on the cache to avoid duplicated calculations. When a given node happens to produce multiple output buffers, we are bound to allocate them for the purpose of this node's {{{process()}}} call, but afterwards we'r just "letting go", releasing the buffers not needed immediately for the channel acutally to be processed. For this to work, it is supposed that the builder has wired in a caching, and that the cache will hit when we touch the same node again for the other channels.
 
 Closely related to this is the problem how to number and identify nodes and thus to be able to find calculated frames in cache (&rarr; [[here|NodeFrameNumbering]])
 
@@ -4990,23 +5028,24 @@ Every node is actually decomposed into three parts
 Thus, the outer container can be changed polymorphically to support the different kinds of nodes (large-scale view). The actual wiring of the nodes is contained in the WiringDescriptor, including the {{{process()}}} function pointer. Additionally, this WiringDescriptor knows the actual type of the operation Strategy, and this actual type has been chosen by the builder such as to select details of the desired operation of this node, for example caching / no caching or maybe ~OpenGL rendering or the special case of a node pulling directly from a source reader. Most of this configuration is done by selecting the right template specialisation within the builder; thus in the critical path most of the calls can be inlined
 
 !!!! composing the actual operation Strategy
-As shown in the class diagram to the right, the actual implementation is assembled by chaining together the various policy classes governing parts of the node operation, like Caching, in-Place calculation capability, etc. (&rarr; see [[here|WiringDescriptor]] for details). The rationale is that the variable part of the Invocation data is allocated at runtime directly on the stack, while a precisely tailored call sequence for "calculating the predecessor nodes" can be defined out of a bunch of simple building blocks. This helps avoiding "spaghetti code", which would be especially dangerous because of the large number of different execution paths to get right. Additionally, a nice side effect of this implementation technique is that a good deal of the implementation is eligible to inlining.
-We //do employ//&nbsp; some virtual calls for the buffer management in order to avoid coupling the policy classes to the actual number of in/out buffers. (As of 6/2008, this is mainly a precaution to be able to control the number of generated template instances. If we ever get in the region of several hundred individual specialisations, we'd need to separate out the allocation of the "buffer table" into a hand-made stack-like buffer allocated from the heap.)
+As shown in the class diagram to the right, the actual implementation is assembled by chaining together the various policy classes governing parts of the node operation, like Caching, in-Place calculation capability, etc. (&rarr; see [[here|WiringDescriptor]] for details). The rationale is that the variable part of the Invocation data is allocated at runtime directly on the stack, while a precisely tailored call sequence for "calculating the predecessor nodes" can be defined out of a bunch of simple building blocks. This helps avoiding "spaghetti code", which would be especially dangerous and difficult to get right because of the large number of different execution paths. Additionally, a nice side effect of this implementation technique is that a good deal of the implementation is eligible to inlining.
+We //do employ//&nbsp; some virtual calls for the buffer management in order to avoid coupling the policy classes to the actual number of in/out buffers. (As of 6/2008, this is mainly a precaution to be able to control the number of generated template instances. If we ever get in the region of several hundred individual specialisations, we'd need to separate out further variable parts to be invoked through virtual functions.)
 
 !Rules for buffer allocation and freeing
 * only output buffers are allocated. It is //never necessary//&nbsp; to allocate input buffers!
 * buffers are to be allocated as late as possible, typically just before invoking {{{process()}}}
-* buffers are allways allocated by calling to the preceeding StateAdapter in the callstack ("parent stae"), because of the possibility of writing the result to cache.
-* {{{pull()}}} returns a handle for the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StateProxy.
-* any other buffers filled with results in the course of the same {{{process()}}} call can be released immediately before returning from the {{{pull()}}}
-* similar, and input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
-* buffers are allways released by calling to the "current state" (which is a StateProxy), providing the buffer-ID to be released
+* buffers are allways allocated by activating a [[buffer handle|BuffHandle]], preconfigured already during the planning phase
+* {{{pull()}}} returns a handle at least for the single output requested by this call, allowing the caller to retrieve the result data
+* any other buffers filled with results during the same {{{process()}}} call can be released immediately before returning from {{{pull()}}}
+* similar, any input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
+* while any handle contains the necessary information for releasing or "committing" this buffer, this has to be triggered explicitly.
 
 @@clear(right):display(block):@@
 
-
+
While the render process, with respect to the dependencies, the builder and the processing function is sufficiently characterized by referring to the ''pull principle'' and by defining a [[protocol|NodeOperationProtocol]] each node has to adhere to &mdash; for actually get it coded we have to care for some important details, especially //how to manage the buffers.// It may well be that the length of the code path necessary to invoke the individual processing functions is finally not so important, compared with the time spent at the inner pixel loop within these functions. But my guess is (as of 5/08), that the overall number of data moving and copying operations //will be//&nbsp; of importance.
+{{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
 !requirements
 * operations should be "in place" as much as possible
@@ -5568,8 +5607,8 @@ if (oldText.indexOf("SplashScreen")==-1)
 }
 //}}}
-
-
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
+
+
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack {{red{Really on the stack? 9/11}}} for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
An Object representing a //Render Process// and containing associated state information.

From 1f13931640fe09f84b798722a6edd2aa95ebe7ea Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 17 Sep 2011 01:50:11 +0200
Subject: [PATCH 068/296] test driven brainstorming: using a BufferProvider

---
 .../engine/diagnostic-buffer-provider.hpp     |  67 ++++++++++
 tests/46engine.tests                          |   5 +
 .../engine/buffer-provider-protocol-test.cpp  | 121 ++++++++++++++++++
 wiki/renderengine.html                        |   4 +-
 4 files changed, 195 insertions(+), 2 deletions(-)
 create mode 100644 src/proc/engine/diagnostic-buffer-provider.hpp
 create mode 100644 tests/components/proc/engine/buffer-provider-protocol-test.cpp

diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp
new file mode 100644
index 000000000..42011673a
--- /dev/null
+++ b/src/proc/engine/diagnostic-buffer-provider.hpp
@@ -0,0 +1,67 @@
+/*
+  DIAGNOSTIC-BUFFER-PROVIDER.hpp  -  helper for testing against the BufferProvider interface
+
+  Copyright (C)         Lumiera.org
+    2011,               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 diagnostic-buffer-provider.hpp
+ ** An facility for writing unit-tests targetting the BufferProvider interface.
+ **
+ ** @see buffer-provider-protocol-test.cpp
+ */
+
+#ifndef PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H
+#define PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H
+
+
+#include "lib/error.hpp"
+#include "proc/engine/buffer-provider.hpp"
+
+#include 
+
+
+namespace engine {
+  
+  
+  /********************************************************************
+   * Helper for unit tests: Buffer provider reference implementation.
+   * 
+   * @todo write type comment
+   */
+  class DiagnosticBufferProvider
+    : public BufferProvider
+    {
+    public:
+      /** build a new Diagnostic Buffer Provider instance,
+       *  discard the existing one. Use the static query API
+       *  for investigating collected data. */
+      static BufferProvider&
+      build()
+        {
+          UNIMPLEMENTED ("Diagnostic Buffer Provider instance");
+        }
+      
+    private:
+      
+    };
+  
+  
+  
+} // namespace engine
+#endif
diff --git a/tests/46engine.tests b/tests/46engine.tests
index 28f68b6a6..bc4097114 100644
--- a/tests/46engine.tests
+++ b/tests/46engine.tests
@@ -2,6 +2,11 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en
 
 
 
+PLANNED "BufferProviderProtocol_test" Buffer provider diagnostics <
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of
+  the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+* *****************************************************/
+
+
+#include "lib/error.hpp"
+#include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
+//#include "lib/util.hpp"
+//#include "proc/play/diagnostic-output-slot.hpp"
+#include "proc/engine/diagnostic-buffer-provider.hpp"
+#include "proc/engine/buffhandle.hpp"
+
+//#include 
+//#include 
+
+//using boost::format;
+//using std::string;
+//using std::cout;
+
+
+namespace engine{
+namespace test  {
+  
+//  using lib::AllocationCluster;
+//  using mobject::session::PEffect;
+  using ::engine::BuffHandle;
+  using lumiera::error::LUMIERA_ERROR_LIFECYCLE;
+  
+  
+  namespace { // Test fixture
+    
+  }
+  
+  
+  /*******************************************************************
+   * @test verify the OutputSlot interface and base implementation
+   *       by performing full data exchange cycle. This is a
+   *       kind of "dry run" for documentation purposes,
+   *       both the actual OutputSlot implementation
+   *       as the client using this slot are Mocks.
+   */
+  class BufferProviderProtocol_test : public Test
+    {
+      virtual void
+      run (Arg) 
+        {
+          UNIMPLEMENTED ("build a diagnostic buffer provider and perform a full lifecycle");
+          verifyStandardCase();
+        }
+      
+      
+      void
+      verifySimpleUsage()
+        {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+          // Create Test fixture.
+          // In real usage, a suitable memory/frame/buffer provider
+          // will be preconfigured, depending on the usage context
+          BufferProvider& provider = DiagnosticBufferProvider::build();
+          
+          BuffHandle buff = provider.lockBufferFor();
+          CHECK (buff.isValid());
+          CHECK (sizeof(TestFrame) <= buff.size());
+          buff.create() = testData(0);
+          
+          TestFrame* storage = *buff;
+          CHECK (testData(0) == *storage);
+          
+          buff.release();
+          CHECK (!buff.isValid());
+          VERIFY_ERROR (LIFECYCLE, *buff );
+          
+          DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider);
+          CHECK (checker.buffer_was_used (0));
+          CHECK (checker.buffer_was_closed (0));
+          CHECK (checker.object_was_attached ());
+          CHECK (checker.object_was_destroyed ());
+          
+          CHECK (testData(0) == checker.accessStorage (0));
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+        }
+      
+      
+      void
+      verifyStandardCase()
+        {
+          // Create Test fixture.
+          // In real usage, a suitable memory/frame/buffer provider
+          // will be preconfigured, depending on the usage context
+          BufferProvider& provider = DiagnosticBufferProvider::build();
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (BufferProviderProtocol_test, "unit player");
+  
+  
+  
+}} // namespace engine::test
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 6cff8960f..0d086a79c 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1121,7 +1121,7 @@ Mostly, the //client code,// i.e. code in need of using buffers, can access some
 
 
-
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
 * that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
 * that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
@@ -1132,7 +1132,7 @@ While BufferProvider is an interface meant to be backed by various different kin
 ;announcing
 :client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations (or similar mechanisms to ensure the avialability) to happen right away; the BufferProvider will then return the actual number of buffers guraanteed to be available. This announcing step is optional an can happen anytime before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. (Currently 9/2011 this is meant to be global for the whole BufferProvider, but it might happen that we need to break that down to individual clients)
 ;locking
-:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again until released. If necessary, the BufferProvider might at that point allocate memory to accomodate (especially when the buffers weren't announced beforehand). The locking might fail and raise an exception. To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
+:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again until released. If necessary, at that point the BufferProvider might allocate memory to accomodate (especially when the buffers weren't announced beforehand). The locking may fail and raise an exception. You may expect failure to be unlikely when buffers have been //anounced beforehand.// To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
 ;attaching
 :optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be automatically destroyed when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, which will be invoked in a similar manner. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
 ;releasing

From e1248d195af07febbdc1c84a317722f62a087059 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 17 Sep 2011 17:29:16 +0200
Subject: [PATCH 069/296] move asside obsoleted code

...will be removed soon, when reworking ProcNode
---
 src/proc/engine/bufftable-obsolete.hpp        | 186 ++++++++++++++++++
 src/proc/engine/nodeinvocation.hpp            |   2 +-
 src/proc/engine/nodeoperation.hpp             |   2 +-
 src/proc/engine/render-invocation.hpp         |   2 +-
 .../proc/engine/buff-table-test.cpp           |   4 +-
 5 files changed, 191 insertions(+), 5 deletions(-)
 create mode 100644 src/proc/engine/bufftable-obsolete.hpp

diff --git a/src/proc/engine/bufftable-obsolete.hpp b/src/proc/engine/bufftable-obsolete.hpp
new file mode 100644
index 000000000..bd79683ee
--- /dev/null
+++ b/src/proc/engine/bufftable-obsolete.hpp
@@ -0,0 +1,186 @@
+/*
+  BUFFTABLE-OBSOLTE.hpp  -  Old dead code to be removed when rewriting ProcNode!!!!!
+
+  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 ENGINE_BUFFHTABLE_OBSOLETE_H
+#define ENGINE_BUFFHTABLE_OBSOLETE_H
+
+
+#include "lib/error.hpp"
+#include "proc/engine/buffhandle.hpp"
+#include "proc/engine/procnode.hpp"
+
+#include 
+#include 
+#include 
+
+
+////////////////////////////////WARNING: obsolete code
+////////////////////////////////WARNING: ...just left in tree to keep it compiling
+////////////////////////////////TICKET   #826  need to be reworked entirely
+
+namespace engine {
+  
+  using std::pair;
+  using std::vector;
+  
+  
+    /**
+     * Obsolete, to be rewritten  /////TICKET #826
+     *  
+     * Tables of buffer handles and corresponding dereferenced buffer pointers.
+     * Used within the invocation of a processing node to calculate data.
+     * The tables are further differentiated into input data buffers and output
+     * data buffers. The tables are supposed to be implemented as bare "C" arrays,
+     * thus the array of real buffer pointers can be fed directly to the
+     * processing function of the respective node.
+     * 
+     * @todo this whole design is a first attempt and rather clumsy. It should be reworked
+     *       to use a single contiguous memory area and just layer the object structure on top
+     *       (by using placement new). Yet the idea of an stack-like organisation should be retained
+     */
+  struct BuffTable
+    {
+      typedef BuffHandle        * PHa;
+      typedef BuffHandle::PBuff * PBu;
+      typedef pair Chunk;
+      
+      PHa outHandle;
+      PHa inHandle;
+      PBu outBuff;
+      PBu inBuff;
+    };
+  
+  
+    /** Obsolete, to be rewritten  /////TICKET #826 */
+  class BuffTableStorage
+    {
+      /////////////////////////////////////////////////////////////////////////TICKET #826  need to be reworked entirely
+      /** just a placeholder to decouple the existing code
+       *  from the reworked BuffHandle logic. The existing
+       *  code in turn will be reworked rather fundamentally
+       */
+      struct BuffHaXXXX
+        : BuffHandle
+        {
+          BuffHaXXXX() : BuffHandle(just_satisfy_the_compiler()) { /* wont work ever */ }
+          static BufferDescriptor const& 
+          just_satisfy_the_compiler() { }
+        };
+        
+                                           ////////////////////////////////////TICKET #825  should be backed by mpool and integrated with node invocation
+      vector        hTab_;
+      vector pTab_;
+      size_t level_;
+      
+    public:
+      BuffTableStorage (const size_t maxSiz)
+        : hTab_(maxSiz),
+          pTab_(maxSiz),
+          level_(0)
+        { }
+      
+      ~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); }
+      
+    protected:
+      
+      friend class BuffTableChunk;
+      
+      /** allocate the given number of slots
+       *  starting at current level to be used
+       *  by the newly created BuffTableChunk
+       */
+      BuffTable::Chunk
+      claim (uint slots)
+        {
+          ASSERT (pTab_.size() == hTab_.size());
+          REQUIRE (level_+slots <= hTab_.size());
+          
+          size_t prev_level (level_);
+          level_ += slots;
+          return std::make_pair (&hTab_[prev_level],
+                                 &pTab_[prev_level]);
+        }
+      
+      void
+      release (uint slots)
+        {
+          ASSERT (slots <= level_);
+          REQUIRE (level_ <= hTab_.size());
+          REQUIRE (level_ <= pTab_.size());
+          
+          level_ -= slots;
+        }
+      
+      bool
+      level_check (BuffTable::Chunk& prev_level)
+        {
+          return prev_level.first  == &hTab_[level_]
+              && prev_level.second == &pTab_[level_];
+        }
+    };
+  
+  
+  /** Obsolete, to be rewritten  /////TICKET #826 
+   * to be allocated on the stack while evaluating a ProcNode#pull() call.
+   * The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
+   * which can be used to crate such chunks. The claiming and releasing of
+   * slots in the BuffTableStorage is automatically tied to BuffTableChunk
+   * object's lifecycle.
+   */
+  class BuffTableChunk
+    : public BuffTable,
+      boost::noncopyable
+    {
+      const uint siz_;
+      BuffTable::Chunk tab_;
+      BuffTableStorage& sto_;
+      
+    public:
+      BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
+        : siz_(wd.nrI + wd.nrO),
+          tab_(storage.claim (siz_)),
+          sto_(storage)
+        {
+          const uint nrO(wd.nrO);
+          
+          // Setup the publicly visible table locations
+          this->outHandle = &tab_.first[ 0 ];
+          this->inHandle  = &tab_.first[nrO];
+          this->outBuff   = &tab_.second[ 0 ];
+          this->inBuff    = &tab_.second[nrO];
+        }
+      
+      ~BuffTableChunk ()
+        {
+          sto_.release (siz_);
+          ASSERT ( sto_.level_check (tab_),
+                  "buffer management logic broken.");
+        }
+    };
+  
+  
+  
+  
+  
+} // namespace engine
+#endif
diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp
index 695406cda..a67696850 100644
--- a/src/proc/engine/nodeinvocation.hpp
+++ b/src/proc/engine/nodeinvocation.hpp
@@ -56,7 +56,7 @@
 #include "proc/state.hpp"
 #include "proc/engine/procnode.hpp"
 #include "proc/engine/buffhandle.hpp"
-#include "proc/engine/bufftable.hpp"
+#include "proc/engine/bufftable-obsolete.hpp"
 
 
 
diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp
index 63753cee6..947fa645f 100644
--- a/src/proc/engine/nodeoperation.hpp
+++ b/src/proc/engine/nodeoperation.hpp
@@ -60,7 +60,7 @@
 #include "proc/state.hpp"
 #include "proc/engine/procnode.hpp"
 #include "proc/engine/buffhandle.hpp"
-#include "proc/engine/bufftable.hpp"
+#include "proc/engine/bufftable-obsolete.hpp"
 #include "proc/engine/nodeinvocation.hpp"
 
 #include "lib/meta/util.hpp"
diff --git a/src/proc/engine/render-invocation.hpp b/src/proc/engine/render-invocation.hpp
index 459eb3f93..481d5e691 100644
--- a/src/proc/engine/render-invocation.hpp
+++ b/src/proc/engine/render-invocation.hpp
@@ -37,7 +37,7 @@
 //#include "proc/state.hpp"
 #include "proc/engine/procnode.hpp"
 #include "proc/engine/buffhandle.hpp"
-//#include "proc/engine/bufftable.hpp"
+//#include "proc/engine/bufftable-obsolete.hpp"
 
 
 
diff --git a/tests/components/proc/engine/buff-table-test.cpp b/tests/components/proc/engine/buff-table-test.cpp
index 267978d49..5d7bd27d3 100644
--- a/tests/components/proc/engine/buff-table-test.cpp
+++ b/tests/components/proc/engine/buff-table-test.cpp
@@ -25,7 +25,7 @@
 #include "lib/error.hpp"
 
 #include "proc/engine/procnode.hpp"
-#include "proc/engine/bufftable.hpp"
+#include "proc/engine/bufftable-obsolete.hpp"
 #include "lib/ref-array.hpp"
 
 #include 
@@ -137,7 +137,7 @@ namespace test  {
       virtual void run(Arg) 
         {
            counter = 0;
-           std::srand (time (NULL));
+           std::srand (std::time (NULL));
            
            // allocate storage block to be used chunk wise
            pStorage.reset (new BuffTableStorage (TABLE_SIZ));

From 32f71cba6d1cc268e0b4754e6f5ced1044aa9009 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 17 Sep 2011 18:25:45 +0200
Subject: [PATCH 070/296] more test-driven brainstorming

---
 src/proc/engine/buffer-provider.cpp           |  6 +--
 src/proc/engine/buffer-provider.hpp           |  4 +-
 src/proc/engine/buffhandle.hpp                |  4 +-
 .../engine/buffer-provider-protocol-test.cpp  | 38 ++++++++++++++++++-
 4 files changed, 44 insertions(+), 8 deletions(-)

diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index 57660a572..c9a2311e8 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -40,7 +40,7 @@ namespace engine {
    *          currently locked and usable by client code
    */
   bool
-  BufferProvider::checkValidity (BufferDescriptor const&)
+  BufferProvider::verifyValidity (BufferDescriptor const&)
   {
     UNIMPLEMENTED ("BufferProvider basic and default implementation");
   }
@@ -58,9 +58,9 @@ namespace engine {
   /* === BufferDescriptor and BuffHandle === */
   
   bool
-  BufferDescriptor::checkValidity()  const
+  BufferDescriptor::verifyValidity()  const
   {
-    return provider_->checkValidity(*this);
+    return provider_->verifyValidity(*this);
   }
   
 
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index 7f8f4f99b..02f6ff262 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -79,12 +79,12 @@ namespace engine {
       
       
       /** describe the kind of buffer managed by this provider */
-      BufferDescriptor getDefaultDescriptor();
+      BufferDescriptor getDefaultDescriptor();                //////////////TODO really? there is no sensible "default"
       
       
       /* === API for BuffHandle internal access === */
       
-      bool checkValidity (BufferDescriptor const&);
+      bool verifyValidity (BufferDescriptor const&);
       
     };
   
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 835d0329c..f2e9646fb 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -79,7 +79,7 @@ namespace engine {
     public:
       // using standard copy operations
       
-      bool checkValidity()  const;
+      bool verifyValidity()  const;
     };
   
   
@@ -143,7 +143,7 @@ namespace engine {
       isValid()  const
         {
           return bool(pBuffer_)
-              && descriptor_.checkValidity();
+              && descriptor_.verifyValidity();
         }
       
     };
diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index c3adb7f9e..19866ebfc 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -24,10 +24,11 @@
 #include "lib/error.hpp"
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
-//#include "lib/util.hpp"
+#include "lib/util-foreach.hpp"
 //#include "proc/play/diagnostic-output-slot.hpp"
 #include "proc/engine/diagnostic-buffer-provider.hpp"
 #include "proc/engine/buffhandle.hpp"
+#include "proc/engine/bufftable.hpp"
 
 //#include 
 //#include 
@@ -35,6 +36,7 @@
 //using boost::format;
 //using std::string;
 //using std::cout;
+using util::for_each;
 
 
 namespace engine{
@@ -48,6 +50,9 @@ namespace test  {
   
   namespace { // Test fixture
     
+    const uint TEST_SIZE = 1024*1024;
+    const uint TEST_ELMS = 20;
+    
   }
   
   
@@ -64,6 +69,7 @@ namespace test  {
       run (Arg) 
         {
           UNIMPLEMENTED ("build a diagnostic buffer provider and perform a full lifecycle");
+          verifySimpleUsage();
           verifyStandardCase();
         }
       
@@ -108,6 +114,36 @@ namespace test  {
           // will be preconfigured, depending on the usage context
           BufferProvider& provider = DiagnosticBufferProvider::build();
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+          
+          BufferDescriptor desc1 = provider.getDescriptor();
+          BufferDescriptor desc2 = provider.getDescriptorFor(TEST_SIZE);
+          CHECK (desc1.verifyValidity());
+          CHECK (desc2.verifyValidity());
+          
+          size_t num1 = provider.announce(TEST_ELMS, desc1);
+          size_t num2 = provider.announce(TEST_ELMS, desc2);
+          CHECK (num1 == TEST_ELMS);
+          CHECK (0 < num2 && num2 <=TEST_ELMS);
+          
+          const size_t STORAGE_SIZE = BuffTable::Storage<2*TEST_ELMS>::size; 
+          char storage[STORAGE_SIZE];
+          BuffTable& tab = BuffTable::prepare(storage, STORAGE_SIZE);
+          
+          for (uint i=0; i < num1; ++i )
+            {
+              tab.attachBuffer (provider.lockBufferFor (desc1));
+            }
+          for (uint i=0; i < num1; ++i )
+            {
+              tab.attachBuffer (provider.lockBufferFor (desc2));
+            }
+          
+          for_each (tab.buffers(), do_some_calculations);
+          
+          tab.releaseBuffers();
+          
+          DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider);
+          CHECK (checker.all_buffers_released());
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
         }
     };

From ed5091d8f5a5c7572ec43fca87c6a750dc5b975c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 18 Sep 2011 01:02:31 +0200
Subject: [PATCH 071/296] considering Buffer handling and BuffTable in more
 detail

---
 .../engine/buffer-provider-protocol-test.cpp  | 17 ++++++---------
 wiki/renderengine.html                        | 21 +++++++++++++++++--
 2 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index 19866ebfc..a50ceb2a5 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -127,19 +127,14 @@ namespace test  {
           
           const size_t STORAGE_SIZE = BuffTable::Storage<2*TEST_ELMS>::size; 
           char storage[STORAGE_SIZE];
-          BuffTable& tab = BuffTable::prepare(storage, STORAGE_SIZE);
-          
-          for (uint i=0; i < num1; ++i )
-            {
-              tab.attachBuffer (provider.lockBufferFor (desc1));
-            }
-          for (uint i=0; i < num1; ++i )
-            {
-              tab.attachBuffer (provider.lockBufferFor (desc2));
-            }
+          BuffTable& tab =
+              BuffTable::prepare(STORAGE_SIZE, storage)
+                        .prepare(num1, desc1)
+                        .prepare(num2, desc2)
+                        .build(); 
           
+          tab.lockBuffers();
           for_each (tab.buffers(), do_some_calculations);
-          
           tab.releaseBuffers();
           
           DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider);
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 0d086a79c..5b6ca80dc 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1144,6 +1144,20 @@ __see also__
 &rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
 
+
+
The invocation of individual [[render nodes|ProcNode]] uses an ''buffer table'' internal helper data structure to encapsulate technical details of the allocation, use, re-use and feeing of data buffers for the media calculations. Here, the management of the physical data buffers is delegated through a BufferProvider, which typically is implemented relying on the ''frame cache'' in the backend. Yet some partially quite involved technical details need to be settled for each invocation: We need input buffers, maybe provided as external input, while in other cases to be filled by a recursive call. We need storage to prepare the (possibly automated) parameters, and finally we need a set of output buffers. All of these buffers and parameters need to be rearranged for invoking the (external) processing function, followed by releasing the input buffers and commiting the output buffers to be used as result.
+
+Because there are several flavours of node wiring, the building blocks comprising such a node invocation will be combined depending on the circumstances. Performing all these various steps is indeed the core concern of the render node -- with the help of BufferTable to deal with the repetitive, tedious and technical details.
+
+!requirements
+The layout of the buffer table will be planned beforehand for each invocation, allongside with planning the individual invocation jobs for the scheduler. At that point, a generic JobTicket for the whole timeline segment is available, describing the necessary operations in an abstract way, as determined by the preceeding planning phase. Jobs are prepared chunk wise, some time in advance (but not all jobs of at once). Jobs will be executed concurrently. Thus, buffer tables need to be created repeatedly and placed into a memory block accessed and owned exclusively by the individual job.
+* within the buffer table, we need an working area for the output handles, the input handles and the parameter descriptors
+* actually, these can be seen as pools holding handle objects which might even be re-used, especially for a chain of effects calculated in-place.
+* each of these pools is characterised by a common //buffer type,// represented as buffer descriptor
+* we need some way to integrate with the StateProxy, because some of the buffers need to be marked especially, e.g. as result
+* there should be convenience functions to release all pending buffers, forwarding the release operation to the individual handles
+
+
//Building the fixture is actually at the core of the [[builder's operation|Builder]]//
 {{red{WIP as of 11/10}}} &rarr; see also the [[planning page|PlanningBuildFixture]]
@@ -3166,7 +3180,7 @@ While the general approach and reasoning remains valid, a lot of the details loo
 In the most general case the render network may be just a DAG (not just a tree). Especially, multiple exit points may lead down to the same node, and following each of this possible paths the node may be at a different depth on each. This rules out a simple counter starting from the exit level, leaving us with the possibility of either employing a rather convoluted addressing scheme or using arbitrary ID numbers.{{red{...which is what we do for now}}}
 
-
+
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. Meaning that there isn't an central entity invoking nodes consecutively. Rather, the nodes themselves contain the detailed knowledg regarding prerequisites, so the calculation plan is worked out recursively. Yet there are some prerequisite resources to be made available for any calculation to happen. Thus the actual calculation is broken down into atomic chunks of work, resulting in a 2-phase invocation whenever "pulling" a node. For this to work, we need the nodes to adhere to a specific protocol:
 ;planning phase
 :when a node invocation is foreseeable to be required for getting a specific frame for a specific nominal and actual time, the engine has to find out the actual operations to happen
@@ -3192,6 +3206,7 @@ some points to note:
 * when a node is "inplace-capable", input and output buffer may actually point to the same location
 * but there is no guarantee for this to happen, because the cache may be involved (and we can't overwrite the contents of a cache frame)
 * generally, a node may have N inputs and M output frames, which are expected to be processed in a single call
+* some of the technical details of buffer management are encapsulated within the BufferTable of each invocation
 
 &rarr; the [["mechanics" of the render process|RenderMechanics]]
 &rarr; more fine grained [[implementation details|RenderImplDetails]]
@@ -5043,7 +5058,7 @@ We //do employ//&nbsp; some virtual calls for the buffer management in order
 @@clear(right):display(block):@@
 
-
+
While the render process, with respect to the dependencies, the builder and the processing function is sufficiently characterized by referring to the ''pull principle'' and by defining a [[protocol|NodeOperationProtocol]] each node has to adhere to &mdash; for actually get it coded we have to care for some important details, especially //how to manage the buffers.// It may well be that the length of the code path necessary to invoke the individual processing functions is finally not so important, compared with the time spent at the inner pixel loop within these functions. But my guess is (as of 5/08), that the overall number of data moving and copying operations //will be//&nbsp; of importance.
 {{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
@@ -5059,6 +5074,8 @@ On the other hand, the processing function within the individual node needs to b
 Not everything can be preconfigured though. The pull principle opens the possibility for the node to decide on a per call base what predecessor(s) to pull (if any). This decision may rely on automation parameters, which thus need to be accessible prior to requesting the buffer(s). Additionally, in a later version we plan to have the node network calculate some control values for adjusting the cache and backend timings &mdash; and of course at some point we'll want to utilize the GPU, resulting in the need to feed data from our processing buffers into some texture representation.
 
 !buffer management
+{{red{NOTE 9/11: the following is partially obsolete and needs to be rewritten}}} &rarr; see the BufferTable for details regarding new buffer management...
+
 Besides the StateProxy representing the actual render process and holding a couple of buffer (refs), we employ a lightweight adapter object in between. It is used //for a single {{{pull()}}}-call// &mdash; mapping the actual buffers to the input and output port numbers of the processing node and for dealing with the cache calls. While the StateProxy manages a pool of frame buffers, this interspersed adapter allows us to either use a buffer retrieved from the cache as an input, possibly use a new buffer located within the cache as output, or (in case no caching happens) to just use the same buffer as input and output for "in-place"-processing. The idea is that most of the configuration of this adapter object is prepared in the wiring step while building the node network.
 
 The usage patern of the buffers can be stack-like when processing nodes require multiple input buffers. In the standard case, which also is the simplest case, a pair of buffers (or a single buffer for "in-place" capable nodes) suffices to calculate a whole chain of nodes. But &mdash; as the recursive descent means depth-first processing &mdash; in case multiple input buffers are needed, we may encounter a situation where some of these input buffers already contain processed data, while we have to descend into yet another predecessor node chain to pull the data for the remaining buffers. Care has to be taken //to allocate the buffers as late as possible,// otherwise we could end up holding onto a buffer almost for each node in the network. Effectively this translates into the rule to allocate output buffers only after all input buffers are ready and filled with data; thus we shouldn't allocate buffers when //entering// the recursive call to the predecessor(s), rather we have to wait until we are about to return from the downcall chain.

From c473784fe3167c9addc6e7a7f2cb6a61ef56c65c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 18 Sep 2011 15:05:18 +0200
Subject: [PATCH 072/296] disable obsolte parts of BufferTable_test

to be rewritten
---
 tests/components/proc/engine/buff-table-test.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tests/components/proc/engine/buff-table-test.cpp b/tests/components/proc/engine/buff-table-test.cpp
index 5d7bd27d3..06ff34e54 100644
--- a/tests/components/proc/engine/buff-table-test.cpp
+++ b/tests/components/proc/engine/buff-table-test.cpp
@@ -81,6 +81,7 @@ namespace test  {
     
     
     
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
     void*
     detect_start_level (BuffTableStorage& sto)
     {
@@ -116,6 +117,7 @@ namespace test  {
           && (not_within(b.inHandle,  b.outBuff, &b.inBuff[num.nrO]))
            ;
     }
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
   } // (End) internal defs
   
   
@@ -142,7 +144,9 @@ namespace test  {
            // allocate storage block to be used chunk wise
            pStorage.reset (new BuffTableStorage (TABLE_SIZ));
            
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
            invocation (0, detect_start_level(*pStorage));
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
            
            pStorage.reset(0);  // dtor throws assertion error if corrupted
            
@@ -161,12 +165,14 @@ namespace test  {
             return; // end recursion
           
           ++counter;
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
           BuffTableChunk thisChunk (numbers, *pStorage);
           CHECK (consistencyCheck (thisChunk, numbers, lastLevel));
           
           uint nrBranches ( 1 + (rand() % WIDTH_MAX));
           while (nrBranches--)
             invocation (consumed, first_behind (thisChunk,numbers.getNrI()));
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
         }
     };
   

From ca615b9933234ad71ddd4a684ebfa284388e47bf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 18 Sep 2011 15:28:58 +0200
Subject: [PATCH 073/296] start stubbing some of the functionality planned thus
 far

---
 src/proc/engine/buffer-provider.hpp           | 21 +++++
 src/proc/engine/buffhandle.hpp                | 25 ++++++
 .../engine/buffer-provider-protocol-test.cpp  |  3 +-
 tests/components/proc/engine/testframe.hpp    | 81 +++++++++++++++++++
 4 files changed, 129 insertions(+), 1 deletion(-)
 create mode 100644 tests/components/proc/engine/testframe.hpp

diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index 02f6ff262..bbb7766ef 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -77,6 +77,9 @@ namespace engine {
       virtual BuffHandle lockBufferFor (BufferDescriptor const&)  =0;
       virtual void releaseBuffer (BuffHandle const&)              =0;
       
+      template
+      BuffHandle lockBufferFor ();
+      
       
       /** describe the kind of buffer managed by this provider */
       BufferDescriptor getDefaultDescriptor();                //////////////TODO really? there is no sensible "default"
@@ -87,7 +90,25 @@ namespace engine {
       bool verifyValidity (BufferDescriptor const&);
       
     };
+    
+    
+    
+    
+  /* === Implementation === */
   
+  /** convenience shortcut:
+   *  prepare and claim ("lock") a buffer suitable
+   *  to hold an object of the given type.
+   * @return a handle embedding a suitably configured
+   *         buffer descriptor. The corresponding buffer
+   *         has been allocated and marked for exclusive use
+   */
+  template
+  BuffHandle
+  BufferProvider::lockBufferFor()
+  {
+    UNIMPLEMENTED ("convenience shortcut to announce and lock for a specific object type");
+  }
   
   
 } // namespace engine
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index f2e9646fb..889638724 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -132,6 +132,10 @@ namespace engine {
       // using standard copy operations
       
       
+      template
+      BU& create();
+      
+      
       Buff&
       operator* ()  const
         {
@@ -146,9 +150,30 @@ namespace engine {
               && descriptor_.verifyValidity();
         }
       
+      size_t
+      size()  const
+        {
+          UNIMPLEMENTED ("forward to the buffer provider for storage size diagnostics");
+        }
+      
     };
   
   
+  /* === Implementation details === */
+  
+  /** convenience shortcut: place and maintain an object within the buffer.
+   *  This operation performs the necessary steps to attach an object;
+   *  if the buffer isn't locked yet, it will do so. Moreover, the created
+   *  object will be owned by the buffer management facilities, i.e. the
+   *  destructor is registered as cleanup function.   
+   */
+  template
+  BU&
+  BuffHandle::create()
+  {
+    UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway");
+  }
+  
   
   
 } // namespace engine
diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index a50ceb2a5..38f65568d 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -26,6 +26,7 @@
 #include "lib/test/test-helper.hpp"
 #include "lib/util-foreach.hpp"
 //#include "proc/play/diagnostic-output-slot.hpp"
+#include "proc/engine/testframe.hpp"
 #include "proc/engine/diagnostic-buffer-provider.hpp"
 #include "proc/engine/buffhandle.hpp"
 #include "proc/engine/bufftable.hpp"
@@ -77,7 +78,6 @@ namespace test  {
       void
       verifySimpleUsage()
         {
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
           // Create Test fixture.
           // In real usage, a suitable memory/frame/buffer provider
           // will be preconfigured, depending on the usage context
@@ -88,6 +88,7 @@ namespace test  {
           CHECK (sizeof(TestFrame) <= buff.size());
           buff.create() = testData(0);
           
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
           TestFrame* storage = *buff;
           CHECK (testData(0) == *storage);
           
diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp
new file mode 100644
index 000000000..0ea6a8d75
--- /dev/null
+++ b/tests/components/proc/engine/testframe.hpp
@@ -0,0 +1,81 @@
+/*
+  TESTFRAME.hpp  -  test data frame (stub) for checking Render engine functionality
+
+  Copyright (C)         Lumiera.org
+    2011,               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_ENGINE_TESTFRAME_H
+#define PROC_ENGINE_TESTFRAME_H
+
+
+//#include "lib/time/timevalue.hpp"
+
+//#include 
+
+
+//using std::tr1::shared_ptr;
+//using std::string;
+
+
+namespace engine {
+namespace test   {
+  
+  
+//class TestPlacement;
+  
+  /**
+   * Mock data frame for simulated rendering.
+   * A test frame can be created and placed instead of a real data frame.
+   * It doesn't depend on any external libraries and will be self-maintaining.
+   * Placeholder functions are provided for assignment (simulating the actual
+   * calculations); additional diagnostic functions allow to verify the
+   * performed operations after-the fact
+   *  
+   * @todo WIP-WIP-WIP 9/11
+   * 
+   */
+  class TestFrame
+    {
+      
+    public:
+    };
+  
+  
+  
+  TestFrame
+  testData (uint seqNr)
+  {
+    UNIMPLEMENTED ("build, memorise and expose test data frames on demand");
+  }
+  
+  TestFrame
+  testData (uint chanNr, uint seqNr)
+  {
+    UNIMPLEMENTED ("build, memorise and expose test data frames on demand (multi-channel)");
+  }
+  
+  
+  
+  /* == some test data to check == */
+  // extern const lib::time::Duration LENGTH_TestClip;
+  
+  
+}} // namespace engine::test
+#endif

From cafe271830c136d613de19e91f6ce8a804c0e4f5 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 19 Sep 2011 01:42:37 +0200
Subject: [PATCH 074/296] stubbing of basic buffer provider functionality

---
 src/proc/engine/buffer-provider.cpp           |  7 ++
 src/proc/engine/buffhandle.hpp                | 19 +++++
 .../engine/diagnostic-buffer-provider.hpp     | 71 +++++++++++++++++++
 .../engine/buffer-provider-protocol-test.cpp  | 14 ++--
 tests/components/proc/engine/testframe.hpp    | 18 +++++
 5 files changed, 121 insertions(+), 8 deletions(-)

diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index c9a2311e8..0ca617577 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -63,6 +63,13 @@ namespace engine {
     return provider_->verifyValidity(*this);
   }
   
+  
+  void
+  BuffHandle::release()
+  {
+    UNIMPLEMENTED ("forward buffer release call to buffer provider");
+  }
+  
 
 
 } // namespace engine
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 889638724..8401bdb17 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -132,9 +132,16 @@ namespace engine {
       // using standard copy operations
       
       
+      
+      void release();
+      
+      
       template
       BU& create();
       
+      template
+      BU& accessAs();
+      
       
       Buff&
       operator* ()  const
@@ -175,6 +182,18 @@ namespace engine {
   }
   
   
+  /** convenience shortcuts: access the buffer contents in a typesafe fashion.
+   *  This is equivalent to a plain dereferentiation with additional metadata check
+   * @throw error::Logic in case of type mismatch \c LUMIERA_ERROR_WRONG_TYPE
+   */
+  template
+  BU&
+  BuffHandle::accessAs()
+  {
+    UNIMPLEMENTED ("convenience shortcut to access buffer contents typesafe");
+  }
+  
+  
   
 } // namespace engine
 #endif
diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp
index 42011673a..c8836b617 100644
--- a/src/proc/engine/diagnostic-buffer-provider.hpp
+++ b/src/proc/engine/diagnostic-buffer-provider.hpp
@@ -47,6 +47,23 @@ namespace engine {
   class DiagnosticBufferProvider
     : public BufferProvider
     {
+      
+      
+      /* === BufferProvider Implementation === */
+      
+      virtual BuffHandle
+      lockBufferFor (BufferDescriptor const& descriptor)
+        {
+          UNIMPLEMENTED ("lock buffer for exclusive use");
+        }
+      
+      virtual void
+      releaseBuffer (BuffHandle const& handle)
+        {
+          UNIMPLEMENTED ("release a buffer and invalidate the handle");
+        }
+      
+      
     public:
       /** build a new Diagnostic Buffer Provider instance,
        *  discard the existing one. Use the static query API
@@ -57,6 +74,60 @@ namespace engine {
           UNIMPLEMENTED ("Diagnostic Buffer Provider instance");
         }
       
+      
+      /** access the diagnostic API of the buffer provider
+       * @throw error::Invalid if the given provider doesn't allow
+       *        for diagnostic access or wasn't registered beforehand.
+       */
+      static DiagnosticBufferProvider&
+      access (BufferProvider const& provider)
+        {
+          UNIMPLEMENTED ("access existing instance linked to the given provider");
+        }
+      
+      
+      
+      
+      /* === diagnostic API === */
+      
+      bool
+      buffer_was_used (uint bufferID)
+        {
+          UNIMPLEMENTED ("check usage flag of a specific buffer");
+        }
+      
+      
+      bool
+      buffer_was_closed (uint bufferID)
+        {
+          UNIMPLEMENTED ("check closed-flag of a specific buffer");
+        }
+      
+      
+      template
+      bool
+      object_was_attached (uint bufferID)
+        {
+          UNIMPLEMENTED ("verify object attachment status of a specific buffer");
+        }
+      
+      
+      template
+      bool
+      object_was_destroyed (uint bufferID)
+        {
+          UNIMPLEMENTED ("verify object attachment status of a specific buffer");
+        }
+      
+      
+      void*
+      accessStorage (uint bufferID)
+        {
+          
+        }
+      
+      
+      
     private:
       
     };
diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index 38f65568d..7846dec5a 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -88,22 +88,20 @@ namespace test  {
           CHECK (sizeof(TestFrame) <= buff.size());
           buff.create() = testData(0);
           
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
-          TestFrame* storage = *buff;
-          CHECK (testData(0) == *storage);
+          TestFrame& storage = buff.accessAs();
+          CHECK (testData(0) == storage);
           
           buff.release();
           CHECK (!buff.isValid());
-          VERIFY_ERROR (LIFECYCLE, *buff );
+          VERIFY_ERROR (LIFECYCLE, buff.accessAs() );
           
-          DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider);
+          DiagnosticBufferProvider& checker = DiagnosticBufferProvider::access(provider);
           CHECK (checker.buffer_was_used (0));
           CHECK (checker.buffer_was_closed (0));
-          CHECK (checker.object_was_attached ());
-          CHECK (checker.object_was_destroyed ());
+          CHECK (checker.object_was_attached (0));
+          CHECK (checker.object_was_destroyed (0));
           
           CHECK (testData(0) == checker.accessStorage (0));
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
         }
       
       
diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp
index 0ea6a8d75..5678bf147 100644
--- a/tests/components/proc/engine/testframe.hpp
+++ b/tests/components/proc/engine/testframe.hpp
@@ -55,6 +55,24 @@ namespace test   {
     {
       
     public:
+      
+      bool
+      operator== (void* memLocation)
+        {
+          UNIMPLEMENTED ("verify contents of an arbitrary memory location");
+        }
+      
+      friend bool
+      operator== (TestFrame const& f1, TestFrame const& f2)
+        {
+          UNIMPLEMENTED ("equality of test data frames");
+        }
+      
+      friend bool
+      operator!= (TestFrame const& f1, TestFrame const& f2)
+        {
+          return !(f1 == f2);
+        }
     };
   
   

From 5b6ecbab1c4b87d22f187676238c83a7de17a606 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 20 Sep 2011 00:54:01 +0200
Subject: [PATCH 075/296] start diagnostic buffer provider implementation

---
 .../engine/diagnostic-buffer-provider.cpp     | 202 ++++++++++++++++++
 .../engine/diagnostic-buffer-provider.hpp     |  65 ++----
 2 files changed, 225 insertions(+), 42 deletions(-)
 create mode 100644 src/proc/engine/diagnostic-buffer-provider.cpp

diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp
new file mode 100644
index 000000000..69df7c35a
--- /dev/null
+++ b/src/proc/engine/diagnostic-buffer-provider.cpp
@@ -0,0 +1,202 @@
+/*
+  DiagnosticBufferProvider  -  helper for testing against the BufferProvider interface
+
+  Copyright (C)         Lumiera.org
+    2011,               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 "lib/error.hpp"
+#include "include/logging.h"
+#include "lib/meta/function.hpp"
+#include "lib/scoped-ptrvect.hpp"
+
+#include "proc/engine/diagnostic-buffer-provider.hpp"
+
+#include 
+//#include 
+
+using lib::ScopedPtrVect;
+using boost::scoped_array;
+
+
+
+namespace engine {
+  
+  
+  /** Storage for the diagnostics frontend */
+  lib::Singleton DiagnosticBufferProvider::diagnostics;
+  
+  
+  class Block
+    : boost::noncopyable
+    {
+      size_t size_;
+      scoped_array storage_;
+      
+      bool was_locked_;
+      
+    public:
+      Block()
+        : size_(0)
+        , storage_()
+        , was_locked_(false)
+        { }
+      
+      bool
+      was_used()  const
+        {
+          return was_locked_;
+        }
+    };
+  
+    
+    
+  namespace { // Details of allocation and accounting
+
+    using lumiera::typelist::FunctionSignature;
+    using std::tr1::placeholders::_1;
+    using std::tr1::function;
+    using std::tr1::bind;
+    
+    /**
+     * type rebinding template for issuing flexible queries
+     * and forwarding them to the individual block entries
+     */
+    template
+    struct _Query
+      {
+        typedef typename FunctionSignature::Ret Result;
+      };
+    
+    function QUERY_was_used = bind (&Block::was_used, _1 );
+  
+  } // (END) Details of allocation and accounting
+  
+  
+  
+  /**
+   * @internal DiagnosticBufferProvider's PImpl.
+   * Uses a linearly growing table of heap allocated buffer blocks,
+   * which will never be discarded, unless the PImpl is discarded as a whole.
+   * This way, the tracked usage information remains available after the fact.
+   */
+  class DiagnosticBufferProvider::HeapMemProvider
+    : public BufferProvider
+    , public ScopedPtrVect
+    {
+      virtual BuffHandle
+      lockBufferFor (BufferDescriptor const& descriptor)
+        {
+          UNIMPLEMENTED ("lock buffer for exclusive use");
+        }
+      
+      virtual void
+      releaseBuffer (BuffHandle const& handle)
+        {
+          UNIMPLEMENTED ("release a buffer and invalidate the handle");
+        }
+      
+    public:
+      HeapMemProvider()
+        {
+        }
+      
+      virtual ~HeapMemProvider()
+        {
+          INFO (proc_mem, "discarding %uz diagnostic buffer entries", HeapMemProvider::size());
+        }
+      
+      template
+      typename _Query::Result
+      accessStats (uint bufferID, FUN queryFunction)
+        {
+          queryFunction (access_or_create (bufferID));
+        }
+      
+    private:
+      Block&
+      access_or_create (uint bufferID)
+        {
+          UNIMPLEMENTED ("either get existing block or create new entries to fit");
+        }
+    };
+  
+  
+  
+  BufferProvider&
+  DiagnosticBufferProvider::build()
+  {
+    return diagnostics().reset();
+  }
+  
+  
+  DiagnosticBufferProvider&
+  DiagnosticBufferProvider::access (BufferProvider const& provider)
+  {
+    if (!diagnostics().isCurrent (provider))
+      throw error::Invalid("given Provider doesn't match (current) diagnostic data record."
+                           "This might be an lifecycle error. Did you build() this instance beforehand?");
+    
+    return diagnostics();
+  }
+  
+
+    
+    
+  DiagnosticBufferProvider::HeapMemProvider&
+  DiagnosticBufferProvider::reset()
+  {
+    pImpl_.reset(new HeapMemProvider());
+    return *pImpl_;
+  }
+  
+  bool
+  DiagnosticBufferProvider::isCurrent (BufferProvider const& implInstance)
+  {
+    return &implInstance == pImpl_.get();
+  }
+
+  
+
+  
+  
+  /* === diagnostic API === */
+  
+  bool
+  DiagnosticBufferProvider::buffer_was_used (uint bufferID)  const
+    {
+      pImpl_->accessStats (bufferID, QUERY_was_used);
+    }
+  
+  
+  bool
+  DiagnosticBufferProvider::buffer_was_closed (uint bufferID)  const
+    {
+      UNIMPLEMENTED ("check closed-flag of a specific buffer");
+    }
+  
+  
+  void*
+  DiagnosticBufferProvider::accessStorage (uint bufferID)  const
+    {
+      
+    }
+  
+
+} // namespace engine
diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp
index c8836b617..399f7d782 100644
--- a/src/proc/engine/diagnostic-buffer-provider.hpp
+++ b/src/proc/engine/diagnostic-buffer-provider.hpp
@@ -31,13 +31,18 @@
 
 
 #include "lib/error.hpp"
+#include "lib/singleton.hpp"
+#include "lib/util.hpp"
 #include "proc/engine/buffer-provider.hpp"
 
+#include 
 #include 
 
 
 namespace engine {
   
+  namespace error = lumiera::error;
+  
   
   /********************************************************************
    * Helper for unit tests: Buffer provider reference implementation.
@@ -45,34 +50,29 @@ namespace engine {
    * @todo write type comment
    */
   class DiagnosticBufferProvider
-    : public BufferProvider
+    : boost::noncopyable
     {
       
+      /**
+       * simple BufferProvider implementation
+       * with additional allocation tracking
+       */
+      class HeapMemProvider;
       
-      /* === BufferProvider Implementation === */
       
-      virtual BuffHandle
-      lockBufferFor (BufferDescriptor const& descriptor)
-        {
-          UNIMPLEMENTED ("lock buffer for exclusive use");
-        }
+      boost::scoped_ptr              pImpl_;
+      static lib::Singleton diagnostics;
       
-      virtual void
-      releaseBuffer (BuffHandle const& handle)
-        {
-          UNIMPLEMENTED ("release a buffer and invalidate the handle");
-        }
+      
+      HeapMemProvider& reset();
+      bool isCurrent (BufferProvider const&);
       
       
     public:
       /** build a new Diagnostic Buffer Provider instance,
        *  discard the existing one. Use the static query API
        *  for investigating collected data. */
-      static BufferProvider&
-      build()
-        {
-          UNIMPLEMENTED ("Diagnostic Buffer Provider instance");
-        }
+      static BufferProvider& build();
       
       
       /** access the diagnostic API of the buffer provider
@@ -80,33 +80,21 @@ namespace engine {
        *        for diagnostic access or wasn't registered beforehand.
        */
       static DiagnosticBufferProvider&
-      access (BufferProvider const& provider)
-        {
-          UNIMPLEMENTED ("access existing instance linked to the given provider");
-        }
+      access (BufferProvider const&);
       
       
       
       
       /* === diagnostic API === */
       
-      bool
-      buffer_was_used (uint bufferID)
-        {
-          UNIMPLEMENTED ("check usage flag of a specific buffer");
-        }
-      
-      
-      bool
-      buffer_was_closed (uint bufferID)
-        {
-          UNIMPLEMENTED ("check closed-flag of a specific buffer");
-        }
+      bool buffer_was_used (uint bufferID)  const;
+      bool buffer_was_closed (uint bufferID)  const;
+      void* accessStorage (uint bufferID)  const;
       
       
       template
       bool
-      object_was_attached (uint bufferID)
+      object_was_attached (uint bufferID)  const
         {
           UNIMPLEMENTED ("verify object attachment status of a specific buffer");
         }
@@ -114,19 +102,12 @@ namespace engine {
       
       template
       bool
-      object_was_destroyed (uint bufferID)
+      object_was_destroyed (uint bufferID)  const
         {
           UNIMPLEMENTED ("verify object attachment status of a specific buffer");
         }
       
       
-      void*
-      accessStorage (uint bufferID)
-        {
-          
-        }
-      
-      
       
     private:
       

From 7ef85065ba8d61f8c08e7e52135a1f864cb02cd2 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 21 Sep 2011 02:23:12 +0200
Subject: [PATCH 076/296] impl: buffer provider metadata...

---
 src/proc/engine/buffer-provider.cpp           | 44 ++++++++++++-
 src/proc/engine/buffer-provider.hpp           | 21 ++++++-
 .../engine/diagnostic-buffer-provider.cpp     | 63 ++++++++++---------
 .../engine/diagnostic-buffer-provider.hpp     |  4 +-
 .../engine/buffer-provider-protocol-test.cpp  |  4 +-
 5 files changed, 97 insertions(+), 39 deletions(-)

diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index 0ca617577..78cded609 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -29,6 +29,46 @@ namespace engine {
   namespace { // impl. details and definitions
     
     const uint DEFAULT_DESCRIPTOR = 0;
+    
+    typedef uint64_t LocalKey;
+    typedef size_t HashVal;
+    
+    const LocalKey UNSPECIFIC = 0;
+    
+    struct TypeHandler
+      {
+        typedef void (*Ctor) (void*);
+        typedef void (*Dtor) (void*);
+        
+        Ctor createAttached;
+        Dtor destroyAttached;
+        
+        TypeHandler()
+          : createAttached (0)
+          , destroyAttached (0)
+          { }
+        
+        template
+        TypeHandler()
+          : createAttached (0)    /////////TODO: how to attach the ctor function??? mabye better use a class with virtual functions?
+          , destroyAttached (0)
+          { }
+      };
+    
+    const TypeHandler RAW_BUFFER;
+    
+    
+    class Metadata
+      {
+      public:
+        static HashVal
+        key ( size_t storageSize
+            , TypeHandler instanceFunc =RAW_BUFFER
+            , LocalKey specifics =UNSPECIFIC)
+        {
+          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
+        }
+      };
   }
 
 
@@ -47,9 +87,9 @@ namespace engine {
   
   
   BufferDescriptor
-  BufferProvider::getDefaultDescriptor()
+  BufferProvider::getDescriptorFor (size_t storageSize)
   {
-    return BufferDescriptor (*this, DEFAULT_DESCRIPTOR);
+    return BufferDescriptor (*this, Metadata::key (storageSize));
   }
       
       
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index bbb7766ef..cb66710d3 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -61,8 +61,11 @@ namespace engine {
   
   
   /**
-   * Handle for a buffer for processing data, abstracting away the actual implementation.
-   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
+   * Interface: a facility providing and managing working buffers for media calculations.
+   * The pointer to actual buffer storage can be retrieved by
+   * - optionally announcing the required buffer(s) beforehand
+   * - "locking" a buffer to yield a buffer handle
+   * - dereferencing this smart-handle class
    * 
    * @todo as of 6/2011 buffer management within the engine is still a bit vague
    */
@@ -82,7 +85,11 @@ namespace engine {
       
       
       /** describe the kind of buffer managed by this provider */
-      BufferDescriptor getDefaultDescriptor();                //////////////TODO really? there is no sensible "default"
+      BufferDescriptor getDescriptorFor(size_t storageSize=0);
+      
+      template
+      BufferDescriptor getDescriptor();
+
       
       
       /* === API for BuffHandle internal access === */
@@ -111,5 +118,13 @@ namespace engine {
   }
   
   
+  template
+  BufferDescriptor
+  BufferProvider::getDescriptor()
+  {
+    UNIMPLEMENTED ("build descriptor for automatically placing an object instance into the buffer");
+  }
+  
+  
 } // namespace engine
 #endif
diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp
index 69df7c35a..b8f527d41 100644
--- a/src/proc/engine/diagnostic-buffer-provider.cpp
+++ b/src/proc/engine/diagnostic-buffer-provider.cpp
@@ -63,28 +63,25 @@ namespace engine {
         {
           return was_locked_;
         }
+      
+      bool
+      was_closed()  const
+        {
+          return was_locked_;
+        }
+      
+      void*
+      accessMemory()  const
+        {
+          return storage_.get();
+        }
     };
   
     
     
   namespace { // Details of allocation and accounting
-
-    using lumiera::typelist::FunctionSignature;
-    using std::tr1::placeholders::_1;
-    using std::tr1::function;
-    using std::tr1::bind;
     
-    /**
-     * type rebinding template for issuing flexible queries
-     * and forwarding them to the individual block entries
-     */
-    template
-    struct _Query
-      {
-        typedef typename FunctionSignature::Ret Result;
-      };
-    
-    function QUERY_was_used = bind (&Block::was_used, _1 );
+    const uint MAX_BUFFERS = 50;
   
   } // (END) Details of allocation and accounting
   
@@ -119,21 +116,27 @@ namespace engine {
       
       virtual ~HeapMemProvider()
         {
-          INFO (proc_mem, "discarding %uz diagnostic buffer entries", HeapMemProvider::size());
+          INFO (proc_mem, "discarding %zu diagnostic buffer entries", HeapMemProvider::size());
         }
       
-      template
-      typename _Query::Result
-      accessStats (uint bufferID, FUN queryFunction)
-        {
-          queryFunction (access_or_create (bufferID));
-        }
-      
-    private:
       Block&
       access_or_create (uint bufferID)
         {
-          UNIMPLEMENTED ("either get existing block or create new entries to fit");
+          while (!withinStorageSize (bufferID))
+            manage (new Block);
+          
+          ENSURE (withinStorageSize (bufferID));
+          return (*this)[bufferID];
+        }
+      
+    private:
+      bool
+      withinStorageSize (uint bufferID)  const
+        {
+          if (bufferID >= MAX_BUFFERS)
+            throw error::Fatal ("hardwired internal limit for test buffers exceeded");
+          
+          return bufferID < size();
         }
     };
   
@@ -181,21 +184,21 @@ namespace engine {
   bool
   DiagnosticBufferProvider::buffer_was_used (uint bufferID)  const
     {
-      pImpl_->accessStats (bufferID, QUERY_was_used);
+      return pImpl_->access_or_create(bufferID).was_used();
     }
   
   
   bool
   DiagnosticBufferProvider::buffer_was_closed (uint bufferID)  const
     {
-      UNIMPLEMENTED ("check closed-flag of a specific buffer");
+      return pImpl_->access_or_create(bufferID).was_closed();
     }
   
   
   void*
-  DiagnosticBufferProvider::accessStorage (uint bufferID)  const
+  DiagnosticBufferProvider::accessMemory (uint bufferID)  const
     {
-      
+      return pImpl_->access_or_create(bufferID).accessMemory();
     }
   
 
diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp
index 399f7d782..f79ca49d2 100644
--- a/src/proc/engine/diagnostic-buffer-provider.hpp
+++ b/src/proc/engine/diagnostic-buffer-provider.hpp
@@ -80,7 +80,7 @@ namespace engine {
        *        for diagnostic access or wasn't registered beforehand.
        */
       static DiagnosticBufferProvider&
-      access (BufferProvider const&);
+      access (BufferProvider const& );
       
       
       
@@ -89,7 +89,7 @@ namespace engine {
       
       bool buffer_was_used (uint bufferID)  const;
       bool buffer_was_closed (uint bufferID)  const;
-      void* accessStorage (uint bufferID)  const;
+      void* accessMemory (uint bufferID)  const;
       
       
       template
diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index 7846dec5a..553f93190 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -101,7 +101,7 @@ namespace test  {
           CHECK (checker.object_was_attached (0));
           CHECK (checker.object_was_destroyed (0));
           
-          CHECK (testData(0) == checker.accessStorage (0));
+          CHECK (testData(0) == checker.accessMemory (0));
         }
       
       
@@ -112,10 +112,10 @@ namespace test  {
           // In real usage, a suitable memory/frame/buffer provider
           // will be preconfigured, depending on the usage context
           BufferProvider& provider = DiagnosticBufferProvider::build();
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
           
           BufferDescriptor desc1 = provider.getDescriptor();
           BufferDescriptor desc2 = provider.getDescriptorFor(TEST_SIZE);
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
           CHECK (desc1.verifyValidity());
           CHECK (desc2.verifyValidity());
           

From 8a2c94014cfafb2b17999e59f09d1a1eeaf1f619 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 22 Sep 2011 18:57:06 +0200
Subject: [PATCH 077/296] extract buffer metadata handling into separate entity

---
 src/proc/engine/buffer-metadata.hpp           | 135 ++++++++++++++++
 src/proc/engine/buffer-provider.cpp           |  41 +----
 tests/46engine.tests                          |   6 +-
 .../proc/engine/buffer-metadata-test.cpp      | 146 ++++++++++++++++++
 4 files changed, 288 insertions(+), 40 deletions(-)
 create mode 100644 src/proc/engine/buffer-metadata.hpp
 create mode 100644 tests/components/proc/engine/buffer-metadata-test.cpp

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
new file mode 100644
index 000000000..ca9d5b734
--- /dev/null
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -0,0 +1,135 @@
+/*
+  BUFFER-METADATA.hpp  -  internal metadata for data buffer providers
+
+  Copyright (C)         Lumiera.org
+    2011,               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 buffer-metadate.hpp
+ ** Metadata for managing and accessing buffers. The Lumiera Engine uses the
+ ** Abstraction of an BufferProvider to handle various kinds of buffer organisation
+ ** and access in a uniform way. Actually, buffers can be exposed and provided by several
+ ** facilities, which might even be implemented through an external library. Thus the engine
+ ** and the abstraction placed in between needs a common set of control data, to be able to
+ ** expose the correct buffer for each request. Typically -- and independent of the actual
+ ** implementation -- the following properties need to be tracked
+ ** - that overall storage size available within the buffer
+ ** - a pair of custom \em creator and \em destructor functions to use together with this buffer
+ ** - an additional client key to distinguish otherwise otherwise identical client requests
+ ** These three distinctions are applied in sequence, thus forming a tree with 3 levels.
+ ** Only the first distinguishing level (the size) is mandatory. The others are provided,
+ ** because some of the foreseeable buffer providers allow to re-access the data placed
+ ** into the buffer, by assigning an internally managed ID to the buffer. The most
+ ** prominent example is the frame cache, which obviously needs to keep track of
+ ** the buffers after the render engine is finished, while the engine code
+ ** just accesses yet another buffer to place the results of calculations.
+ ** 
+ ** These additional distinctions and properties are associated with the help of the
+ ** BufferDescriptor, embedded into each BuffHandle. While the engine just uses these
+ ** handles in the way of a pointer, the buffer descriptor acts as an additional tag
+ ** attached to the buffer access, allowing to re-access a context within the
+ ** buffer provider implementation.
+ ** 
+ ** @see buffer-provider.hpp
+ ** @see BufferMetadata_test
+ ** @see BufferProviderProtocol_test
+ */
+
+#ifndef PROC_ENGINE_BUFFR_METADATA_H
+#define PROC_ENGINE_BUFFR_METADATA_H
+
+
+#include "lib/error.hpp"
+
+//#include 
+
+
+namespace engine {
+  
+  
+  typedef uint64_t LocalKey;
+  typedef size_t HashVal;
+  
+  const LocalKey UNSPECIFIC = 0;
+  
+  struct TypeHandler
+    {
+      typedef void (*Ctor) (void*);
+      typedef void (*Dtor) (void*);
+      
+      Ctor createAttached;
+      Dtor destroyAttached;
+      
+      TypeHandler()
+        : createAttached (0)
+        , destroyAttached (0)
+        { }
+      
+      template
+      TypeHandler()
+        : createAttached (0)    /////////TODO: how to attach the ctor function??? mabye better use a class with virtual functions?
+        , destroyAttached (0)
+        { }
+    };
+  
+  const TypeHandler RAW_BUFFER;
+  
+  
+  class Metadata
+    {
+    public:
+      static HashVal
+      key ( size_t storageSize
+          , TypeHandler instanceFunc =RAW_BUFFER
+          , LocalKey specifics =UNSPECIFIC)
+        {
+          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
+        }
+      
+      static Metadata&
+      get (HashVal key)
+        {
+          UNIMPLEMENTED ("access, possibly create metadata records");
+        }
+      
+      BufferState
+      state ()  const
+        {
+          UNIMPLEMENTED ("buffer state accounting");
+        }
+      
+      Metadata&
+      mark (BufferState newState)
+        {
+          UNIMPLEMENTED ("buffer state transitions");
+          return *this;
+        }
+    };
+    
+    
+    
+    
+  /* === Implementation === */
+  
+  /** */
+  
+  
+  
+  
+} // namespace engine
+#endif
diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index 78cded609..d2820d211 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -22,6 +22,8 @@
 
 
 #include "proc/engine/buffer-provider.hpp"
+#include "proc/engine/buffer-metadata.hpp"
+
 
 namespace engine {
   
@@ -30,45 +32,6 @@ namespace engine {
     
     const uint DEFAULT_DESCRIPTOR = 0;
     
-    typedef uint64_t LocalKey;
-    typedef size_t HashVal;
-    
-    const LocalKey UNSPECIFIC = 0;
-    
-    struct TypeHandler
-      {
-        typedef void (*Ctor) (void*);
-        typedef void (*Dtor) (void*);
-        
-        Ctor createAttached;
-        Dtor destroyAttached;
-        
-        TypeHandler()
-          : createAttached (0)
-          , destroyAttached (0)
-          { }
-        
-        template
-        TypeHandler()
-          : createAttached (0)    /////////TODO: how to attach the ctor function??? mabye better use a class with virtual functions?
-          , destroyAttached (0)
-          { }
-      };
-    
-    const TypeHandler RAW_BUFFER;
-    
-    
-    class Metadata
-      {
-      public:
-        static HashVal
-        key ( size_t storageSize
-            , TypeHandler instanceFunc =RAW_BUFFER
-            , LocalKey specifics =UNSPECIFIC)
-        {
-          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
-        }
-      };
   }
 
 
diff --git a/tests/46engine.tests b/tests/46engine.tests
index bc4097114..2c9df05db 100644
--- a/tests/46engine.tests
+++ b/tests/46engine.tests
@@ -7,7 +7,11 @@ return: 0
 END
 
 
-PLANNED "BuffTable_test" BuffTable_test <
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of
+  the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+* *****************************************************/
+
+
+#include "lib/error.hpp"
+#include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
+//#include "lib/util-foreach.hpp"
+#include "lib/util.hpp"
+//#include "proc/play/diagnostic-output-slot.hpp"
+//#include "proc/engine/testframe.hpp"
+//#include "proc/engine/diagnostic-buffer-provider.hpp"
+#include "proc/engine/buffer-metadata.hpp"
+//#include "proc/engine/buffhandle.hpp"
+//#include "proc/engine/bufftable.hpp"
+
+//#include 
+#include 
+//#include 
+
+//using boost::format;
+//using std::string;
+//using std::cout;
+//using util::for_each;
+using util::isnil;
+using util::isSameObject;
+
+
+namespace engine{
+namespace test  {
+  
+//  using lib::AllocationCluster;
+//  using mobject::session::PEffect;
+//  using ::engine::BuffHandle;
+  using lumiera::error::LUMIERA_ERROR_INVALID;
+  using lumiera::error::LUMIERA_ERROR_LIFECYCLE;
+  
+  
+  namespace { // Test fixture
+    
+    const size_t TEST_MAX_SIZE = 1024 * 1024;
+    
+    const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE 
+    const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE 
+    
+    const HashVal JUST_SOMETHING = 123;
+//  const uint TEST_SIZE = 1024*1024;
+//  const uint TEST_ELMS = 20;
+      
+    bool
+    ensure_proper_fixture() 
+    {
+      return (SIZE_A != SIZE_B)
+          && (JUST_SOMETHING != Metadata::key(SIZE_A))
+          && (JUST_SOMETHING != Metadata::key(SIZE_B))
+          ;
+    }
+    
+  }
+  
+  
+  /*******************************************************************
+   * @test verify the properties of the BufferMetadata records used
+   *       internally within BufferProvider to attach additional
+   *       organisational data to the exposed buffers.
+   */
+  class BufferMetadata_test : public Test
+    {
+      virtual void
+      run (Arg) 
+        {
+          UNIMPLEMENTED ("cover all metadata properties");
+          CHECK (ensure_proper_fixture());
+          verifyBasicProperties();
+          verifyStandardCase();
+        }
+      
+      
+      void
+      verifyBasicProperties()
+        {
+          HashVal key = Metadata::key(SIZE_A);
+          CHECK (key);
+          
+          HashVal key1 = Metadata::key(SIZE_A);
+          HashVal key2 = Metadata::key(SIZE_B);
+          CHECK (key1);
+          CHECK (key2);
+          CHECK (key == key1);
+          CHECK (key != key2);
+          
+          VERIFY_ERROR (INVALID, Metadata::get(0))
+          VERIFY_ERROR (INVALID, Metadata::get(JUST_SOMETHING));
+          CHECK ( & Metadata::get(key));
+          CHECK ( & Metadata::get(key1));
+          CHECK ( & Metadata::get(key2));
+          
+          CHECK ( isSameObject (Metadata::get(key), Metadata::get(key)));
+          CHECK ( isSameObject (Metadata::get(key), Metadata::get(key1)));
+          CHECK (!isSameObject (Metadata::get(key), Metadata::get(key2)));
+          
+          Metadata& m1 = Metadata::get(key);
+          CHECK (NIL == m1.state());
+          
+          VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
+          
+          m1.mark (LOCKED);
+          CHECK (LOCKED == m1.state());
+          CHECK (LOCKED == Metadata::get(key1).state());
+        }
+      
+      
+      void
+      verifyStandardCase()
+        {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (BufferMetadata_test, "unit player");
+  
+  
+  
+}} // namespace engine::test

From 1ea3cba2f5237e68de28788f35375b8460165663 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 23 Sep 2011 15:31:20 +0200
Subject: [PATCH 078/296] remold the buffer metadata into a PImple used by
 BufferProvider

---
 src/proc/engine/buffer-metadata.hpp           | 51 ++++++++++++-----
 src/proc/engine/buffer-provider.cpp           |  8 ++-
 src/proc/engine/buffer-provider.hpp           | 17 +++---
 .../engine/diagnostic-buffer-provider.cpp     |  4 +-
 .../proc/engine/buffer-metadata-test.cpp      | 57 +++++++++++--------
 5 files changed, 89 insertions(+), 48 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index ca9d5b734..13cc9432b 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -55,16 +55,27 @@
 
 
 #include "lib/error.hpp"
+#include "lib/symbol.hpp"
 
 //#include 
 
 
 namespace engine {
   
+  using lib::Literal;
+  
   
   typedef uint64_t LocalKey;
   typedef size_t HashVal;
   
+  enum BufferState
+    { NIL,
+      FREE,
+      LOCKED,
+      EMITTED,
+      BLOCKED
+    };
+  
   const LocalKey UNSPECIFIC = 0;
   
   struct TypeHandler
@@ -93,7 +104,17 @@ namespace engine {
   class Metadata
     {
     public:
-      static HashVal
+      struct Entry
+        {
+          BufferState state ()  const;
+          Entry& mark (BufferState newState);
+        };
+      
+      
+      Metadata (Literal implementationID)
+        { }
+      
+      HashVal
       key ( size_t storageSize
           , TypeHandler instanceFunc =RAW_BUFFER
           , LocalKey specifics =UNSPECIFIC)
@@ -101,24 +122,14 @@ namespace engine {
           UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
         }
       
-      static Metadata&
+      Entry&
       get (HashVal key)
         {
           UNIMPLEMENTED ("access, possibly create metadata records");
         }
       
-      BufferState
-      state ()  const
-        {
-          UNIMPLEMENTED ("buffer state accounting");
-        }
-      
-      Metadata&
-      mark (BufferState newState)
-        {
-          UNIMPLEMENTED ("buffer state transitions");
-          return *this;
-        }
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
     };
     
     
@@ -127,6 +138,18 @@ namespace engine {
   /* === Implementation === */
   
   /** */
+  BufferState
+  Metadata::Entry::state ()  const
+    {
+      UNIMPLEMENTED ("buffer state accounting");
+    }
+  
+  Metadata::Entry&
+  Metadata::Entry::mark (BufferState newState)
+    {
+      UNIMPLEMENTED ("buffer state transitions");
+      return *this;
+    }
   
   
   
diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index d2820d211..e0670cd73 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -34,7 +34,11 @@ namespace engine {
     
   }
 
-
+  
+  BufferProvider::BufferProvider (Literal implementationID)
+    : meta_(new Metadata (implementationID))
+    { }
+  
   BufferProvider::~BufferProvider() { }
   
   
@@ -52,7 +56,7 @@ namespace engine {
   BufferDescriptor
   BufferProvider::getDescriptorFor (size_t storageSize)
   {
-    return BufferDescriptor (*this, Metadata::key (storageSize));
+    return BufferDescriptor (*this, meta_->key (storageSize));
   }
       
       
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index cb66710d3..6f025a1c8 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -43,21 +43,20 @@
 
 
 #include "lib/error.hpp"
+#include "lib/symbol.hpp"
 #include "proc/engine/buffhandle.hpp"
 
 #include 
+#include 
 
 
 namespace engine {
   
+  using boost::scoped_ptr;
+  using lib::Literal;
   
-  enum BufferState
-    { NIL,
-      FREE,
-      LOCKED,
-      EMITTED,
-      BLOCKED
-    };
+  
+  class Metadata;
   
   
   /**
@@ -72,6 +71,10 @@ namespace engine {
   class BufferProvider
     : boost::noncopyable
     {
+      scoped_ptr meta_;
+      
+    protected:
+      BufferProvider (Literal implementationID);
       
     public:
       virtual ~BufferProvider();  ///< this is an interface
diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp
index b8f527d41..1711e1a6b 100644
--- a/src/proc/engine/diagnostic-buffer-provider.cpp
+++ b/src/proc/engine/diagnostic-buffer-provider.cpp
@@ -111,8 +111,8 @@ namespace engine {
       
     public:
       HeapMemProvider()
-        {
-        }
+        : BufferProvider ("Diagnostic_HeapAllocated")
+        { }
       
       virtual ~HeapMemProvider()
         {
diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp
index a8573bfd0..839307aad 100644
--- a/tests/components/proc/engine/buffer-metadata-test.cpp
+++ b/tests/components/proc/engine/buffer-metadata-test.cpp
@@ -34,6 +34,7 @@
 //#include "proc/engine/bufftable.hpp"
 
 //#include 
+#include 
 #include 
 //#include 
 
@@ -41,6 +42,7 @@
 //using std::string;
 //using std::cout;
 //using util::for_each;
+using boost::scoped_ptr;
 using util::isnil;
 using util::isSameObject;
 
@@ -48,6 +50,7 @@ using util::isSameObject;
 namespace engine{
 namespace test  {
   
+
 //  using lib::AllocationCluster;
 //  using mobject::session::PEffect;
 //  using ::engine::BuffHandle;
@@ -59,21 +62,13 @@ namespace test  {
     
     const size_t TEST_MAX_SIZE = 1024 * 1024;
     
-    const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE 
-    const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE 
+    const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE;
+    const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE;
     
     const HashVal JUST_SOMETHING = 123;
 //  const uint TEST_SIZE = 1024*1024;
 //  const uint TEST_ELMS = 20;
       
-    bool
-    ensure_proper_fixture() 
-    {
-      return (SIZE_A != SIZE_B)
-          && (JUST_SOMETHING != Metadata::key(SIZE_A))
-          && (JUST_SOMETHING != Metadata::key(SIZE_B))
-          ;
-    }
     
   }
   
@@ -85,6 +80,9 @@ namespace test  {
    */
   class BufferMetadata_test : public Test
     {
+      /** common Metadata table to be tested */
+      scoped_ptr meta_;
+    
       virtual void
       run (Arg) 
         {
@@ -95,37 +93,50 @@ namespace test  {
         }
       
       
+      bool
+      ensure_proper_fixture() 
+      {
+        if (!meta_)
+          meta_.reset(new Metadata("BufferMetadata_test"));
+        
+        return (SIZE_A != SIZE_B)
+            && (JUST_SOMETHING != meta_->key(SIZE_A))
+            && (JUST_SOMETHING != meta_->key(SIZE_B))
+            ;
+      }
+      
+      
       void
       verifyBasicProperties()
         {
-          HashVal key = Metadata::key(SIZE_A);
+          HashVal key = meta_->key(SIZE_A);
           CHECK (key);
           
-          HashVal key1 = Metadata::key(SIZE_A);
-          HashVal key2 = Metadata::key(SIZE_B);
+          HashVal key1 = meta_->key(SIZE_A);
+          HashVal key2 = meta_->key(SIZE_B);
           CHECK (key1);
           CHECK (key2);
           CHECK (key == key1);
           CHECK (key != key2);
           
-          VERIFY_ERROR (INVALID, Metadata::get(0))
-          VERIFY_ERROR (INVALID, Metadata::get(JUST_SOMETHING));
-          CHECK ( & Metadata::get(key));
-          CHECK ( & Metadata::get(key1));
-          CHECK ( & Metadata::get(key2));
+          VERIFY_ERROR (INVALID, meta_->get(0))
+          VERIFY_ERROR (INVALID, meta_->get(JUST_SOMETHING));
+          CHECK ( & meta_->get(key));
+          CHECK ( & meta_->get(key1));
+          CHECK ( & meta_->get(key2));
           
-          CHECK ( isSameObject (Metadata::get(key), Metadata::get(key)));
-          CHECK ( isSameObject (Metadata::get(key), Metadata::get(key1)));
-          CHECK (!isSameObject (Metadata::get(key), Metadata::get(key2)));
+          CHECK ( isSameObject (meta_->get(key), meta_->get(key)));
+          CHECK ( isSameObject (meta_->get(key), meta_->get(key1)));
+          CHECK (!isSameObject (meta_->get(key), meta_->get(key2)));
           
-          Metadata& m1 = Metadata::get(key);
+          Metadata::Entry& m1 = meta_->get(key);
           CHECK (NIL == m1.state());
           
           VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
           
           m1.mark (LOCKED);
           CHECK (LOCKED == m1.state());
-          CHECK (LOCKED == Metadata::get(key1).state());
+          CHECK (LOCKED == meta_->get(key1).state());
         }
       
       

From 5188982e7118edbb13929a21cea20252fed00b51 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 24 Sep 2011 02:27:12 +0200
Subject: [PATCH 079/296] considering details regarding the usage of buffer
 metadata

---
 wiki/renderengine.html | 38 +++++++++++++++++++++++++++-----------
 1 file changed, 27 insertions(+), 11 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 5b6ca80dc..42a7cb353 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1077,8 +1077,8 @@ Please note the shortcomings and logical contradictions in the solution currentl
 * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
-
-
All rendering, transformations and output of media data requires using ''data buffers'' -- but the actual layout and handling of these buffers is closely related to the actual implementation of these operations. As we're relying heavily on external libraries and plug-ins for performing these, there is no hope getting away with just one single {{{Buffer}}} data type definition. Thus, we need to confine ourselves to a common denominator of basic operations regarding data buffers and abstract the access to these operations through a BufferProvider entity. Beyond these basic operations, mostly we just need to assure that //a buffer exists as an distinguishable element// -- which in practice boils down to pushing around {{{void*}}} variables. 
+
+
All rendering, transformations and output of media data requires using ''data buffers'' -- but the actual layout and handling of these buffers is closely related to the actual implementation of these operations. As we're relying heavily on external libraries and plug-ins for performing these operations, there is no hope getting away with just one single {{{Buffer}}} data type definition. Thus, we need to confine ourselves to a common denominator of basic operations regarding data buffers and abstract the access to these operations through a BufferProvider entity. Beyond these basic operations, mostly we just need to assure that //a buffer exists as an distinguishable element// -- which in practice boils down to pushing around {{{void*}}} variables. 
 
 Obviously, overloading a pointer with semantic meaning isn't exactly a brilliant idea -- and the usual answer is to embed this pointer into a smart handle, which also yields the nice side-effect of explaining this design to the reader. Thus a buffer handle
 * can only be obtained from a BufferProvider
@@ -1091,7 +1091,7 @@ To perform anything useful with such a buffer handle, the client code needs some
 
 Just linking this type information to the context is certainly the most elegant solution, but also by far the most difficult to achieve -- not to mention the implicit dependency on a very specific invocation situation. So for now (9/2011) it seems best to stick to the simple and explicit implementation, just keeping that structural optimisation in mind. And the link to this buffer type information should be made explicit within the definition anyway, even if we choose to employ another design tradeoff later.
 * thus the conclusion is: we introduce a ''descriptor object'', which will be stored within the handle
-* each BufferProvider exposes a ''descriptor prototype''; it can be specialised and used by to organise implementation details
+* each BufferProvider exposes a ''descriptor prototype''; it can be specialised and used by to [[organise implementation details|BufferMetadata]]
 
 
 !sanity checks
@@ -1099,7 +1099,7 @@ there are only limited sanity checks, and they can be expected to be optimised a
 Basically the client is responsible for sane buffer access.
 
-
+
Buffers are used to hold the media data for processing and output. Within the Lumiera RenderEngine and [[Player]] subsystem, we use some common concepts to handle the access and allocation of working buffers. Yet this doesn't imply having only one central authority in charge of every buffer -- such an approach wouldn't be possible (due to collaboration with external systems) and wouldn't be desirable either. Rather, there are some common basic usage //patterns// -- and there are some core interfaces used throughout the organisation of the rendering process.
 
 Mostly, the //client code,// i.e. code in need of using buffers, can access some BufferProvider, thereby delegating the actual buffer management. This binds the client to adhere to kind of a //buffer access protocol,// comprised of the ''announcing'', ''locking'', optionally ''attaching'' and finally the ''releasing'' steps. Here, the actual buffer management within the provider is a question of implementation and will be configured during build-up of the scope in question.
@@ -1115,13 +1115,25 @@ Mostly, the //client code,// i.e. code in need of using buffers, can access some
 :in all those situations, where we just need a working buffer for some time, we can rely on our internal custom memory allocator.
 :{{red{~Not-Yet-Implemented as of 9/11}}} -- as a fallback we just rely on heap allocations through the language runtime
 ;frame cache
-:whenever a calculated result may be of further interest beyond the immediate need triggering the calculation, it might be eligible for caching.
+:whenever a calculated result may be of further interest, beyond the immediate need triggering the calculation, it might be eligible for caching.
 :The Lumiera ''frame cache'' is a special BufferProvider, maintaining a larger pool of buffers which can be pinned and kept around for some time,
 :accomodating limited resources and current demand for fresh result buffers.
 
 
-
+
+
the generic BufferProvider implementation exposes a service to attach and maintain additional metadata with individual buffers. Using this service is not mandatory -- a concrete buffer provider implementation may chose to maintain more specific metadata right on the implementation level, especially if more elaborate management is necessary within the implementation anyway (e.g. the frame index). We can expect most buffer provider implementations to utilise at least the generic buffer type id service though.
+
+!buffer types and descriptors
+Client code accesses buffer through [[smart buffer handles|BuffHandle]], including some kind of buffer type information, encoded into a type ID within the ''buffer descriptor''. These descriptors are used like prototypes, relating the type-~IDs hierarchically. Obviously, the most fundamental distinction is the BufferProvider in charge for that specific buffer. Below that, the next mandatory level of distinction is the ''buffer size''. In some cases, additional distinctions can be necessary. Each BufferProvider exposes a service to yield unique type ~IDs governed by such a hierarchical scheme.
+
+!state and metadata for individual buffers
+Beyond that, it can be necessary to associate at least a state flag with //individual buffers.// Doing so requires the buffer to be in //locked state,// otherwise it wouldn't be distinguishable as an separate entity (a client is able to access the buffer memory address only after "locking" this buffer). Especially when using a buffer provider in conjunction with an OutputSlot, these states and transitions are crucial for performing an orderly handover of generated data from the producer (render engine) to the consumer (external output sink).
+
+__Note__: while the API to access this service is uniform, conceptually there is a difference between just using the (shared) type information and associating individual metadata, like the buffer state. Type-~IDs, once allocated, will never be discarded (within the lifetime of an Lumiera application instance -- buffer associations aren't persistent). To the contrary, individual metadata //will be discarded,// when releasing the corresponding buffer. According to the ''prototype pattern'', individual metadata is treated as a one-way-off specialisation.
+
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
 * that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
 * that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
@@ -1130,14 +1142,18 @@ Mostly, the //client code,// i.e. code in need of using buffers, can access some
 !operations
 While BufferProvider is an interface meant to be backed by various different kinds of buffer and memory management approaches, there is a common set of operations to be supported by any of them
 ;announcing
-:client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations (or similar mechanisms to ensure the avialability) to happen right away; the BufferProvider will then return the actual number of buffers guraanteed to be available. This announcing step is optional an can happen anytime before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. (Currently 9/2011 this is meant to be global for the whole BufferProvider, but it might happen that we need to break that down to individual clients)
+:client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations to happen right away, or it might trigger similar mechanisms to ensure availability; the BufferProvider will then return the actual number of buffers guaranteed to be available. This announcing step is optional an can happen any time before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. (Currently 9/2011 this is meant to be global for the whole BufferProvider, but it might happen that we need to break that down to individual clients)
 ;locking
-:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again until released. If necessary, at that point the BufferProvider might allocate memory to accomodate (especially when the buffers weren't announced beforehand). The locking may fail and raise an exception. You may expect failure to be unlikely when buffers have been //anounced beforehand.// To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
+:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again unless released. If necessary, at that point the BufferProvider might allocate memory to accommodate (especially when the buffers weren't announced beforehand). The locking may fail and raise an exception. You may expect failure to be unlikely when buffers have been //announced beforehand.// To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
 ;attaching
-:optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be automatically destroyed when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, which will be invoked in a similar manner. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
+:optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be destroyed automatically when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, to be invoked in a similar way. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
 ;releasing
 :buffers need to be released explicitly by the client code. This renders the corresponding BuffHandle invalid, (optionally) invokes a destructor function of an attached object and maybe reclaims the buffer memory
 
+!!type metadata service
+In addition to the basic operations, clients may associate BufferMetadata with individual buffers;
+in the basic form, this means just maintaining a type tag describing the kind of buffer, while optionally this service might be extended to e.g. associating a state flag.
+
 __see also__
 &rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
 &rarr; more about BufferManagement within the RenderEngine and [[Player]] subsystem
@@ -3480,8 +3496,8 @@ Thus there are two serious problem situations
 &rarr; SchedulerRequirements
 &rarr; OutputSlotImpl
-
-
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot is outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
+
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot will be outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
 
 !operation steps
 [>img[Sequenz of output data exchange steps|uml/fig145157.png]]The OutputSlot class defines some standard operations as protected virtual functions. These represent the actual steps in the data exchange protocol corresponding to this output slot.

From bc756e42d93007d23154db37ed9e0957ca597039 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 24 Sep 2011 15:27:58 +0200
Subject: [PATCH 080/296] rewrite (planned) test to reflect the new metadata
 design

---
 src/proc/engine/buffer-metadata.hpp           | 47 ++++++++++++++++++-
 tests/46engine.tests                          |  6 +--
 .../proc/engine/buffer-metadata-test.cpp      | 26 ++++++++--
 3 files changed, 72 insertions(+), 7 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index 13cc9432b..c71359313 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -106,8 +106,10 @@ namespace engine {
     public:
       struct Entry
         {
-          BufferState state ()  const;
+          BufferState state()  const;
+          HashVal parentKey()  const;
           Entry& mark (BufferState newState);
+          Entry& markLocked (const void* buffer);
         };
       
       
@@ -122,12 +124,42 @@ namespace engine {
           UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
         }
       
+      HashVal
+      key (HashVal parentKey, TypeHandler instanceFunc)
+        {
+          UNIMPLEMENTED ("create sub-type key");
+        }
+      
+      HashVal
+      key (HashVal parentKey, LocalKey specifics)
+        {
+          UNIMPLEMENTED ("create sub-type key");
+        }
+      
+      HashVal
+      key (HashVal parentKey, const void* concreteBuffer)
+        {
+          UNIMPLEMENTED ("create sub-object key for concrete buffer");
+        }
+      
       Entry&
       get (HashVal key)
         {
           UNIMPLEMENTED ("access, possibly create metadata records");
         }
       
+      bool
+      isKnown (HashVal key)  const
+        {
+          UNIMPLEMENTED ("diagnostics: known record?");
+        }
+      
+      bool
+      isLocked (HashVal key)  const
+        {
+          UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
+        }
+      
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
     };
@@ -144,6 +176,13 @@ namespace engine {
       UNIMPLEMENTED ("buffer state accounting");
     }
   
+  /** */
+  HashVal
+  Metadata::Entry::parentKey ()  const
+    {
+      UNIMPLEMENTED ("retrieve the sparent (object or type) key");
+    }
+  
   Metadata::Entry&
   Metadata::Entry::mark (BufferState newState)
     {
@@ -151,6 +190,12 @@ namespace engine {
       return *this;
     }
   
+  Metadata::Entry&
+  Metadata::Entry::markLocked (const void* buffer)
+    {
+      UNIMPLEMENTED ("transition to locked state");
+      return *this;
+    }
   
   
   
diff --git a/tests/46engine.tests b/tests/46engine.tests
index 2c9df05db..e1a3f9704 100644
--- a/tests/46engine.tests
+++ b/tests/46engine.tests
@@ -2,16 +2,16 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en
 
 
 
-PLANNED "BufferProviderProtocol_test" Buffer provider diagnostics <get(key);
           CHECK (NIL == m1.state());
+          CHECK (!meta_->isLocked(key));
           
           VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
+          VERIFY_ERROR (LIFECYCLE, m1.mark(LOCKED)  );
           
-          m1.mark (LOCKED);
-          CHECK (LOCKED == m1.state());
-          CHECK (LOCKED == meta_->get(key1).state());
+          Metadata::Entry& m2 = m1.markLocked (SOME_POINTER);
+          CHECK (!isSameObject (m1,m2));
+          CHECK (NIL    == m1.state());
+          CHECK (LOCKED == m2.state());
+          
+          HashVal keyX = meta_->key(key1, SOME_POINTER);
+          CHECK (meta_->isLocked(keyX));
+          CHECK (keyX != key1);
+          CHECK (keyX);
+          
+          CHECK ( isSameObject (m1, meta_->get(key)));
+          CHECK ( isSameObject (m1, meta_->get(key1))); 
+          CHECK ( isSameObject (m2, meta_->get(keyX)));
+          CHECK ( key1 == m2.parentKey());
+          
+          m2.mark(FREE);              // Warning: don't use the m2 reference anymore! 
+          CHECK (!meta_->isLocked(keyX));
+          CHECK (!meta_->isKnown(keyX));
+          CHECK ( meta_->isKnown(key1));
+          VERIFY_ERROR (INVALID, meta_->get(keyX));
         }
       
       

From db2b02f0c51c46e0f141ee94727bda6d806cac37 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Sep 2011 04:07:30 +0200
Subject: [PATCH 081/296] define a front-end for explicit allocations

...currently just defined by forwarding
to std::allocator
---
 src/lib/result.hpp                            |   6 +-
 src/lib/scoped-holder-transfer.hpp            |   4 +-
 src/lib/simple-allocator.hpp                  | 262 ++++++++++++++++++
 src/lib/typed-allocation-manager.hpp          |   4 +-
 src/proc/control/command-argument-holder.hpp  |   1 -
 src/proc/control/command-impl.hpp             |   1 -
 src/proc/engine/buffer-metadata.hpp           |   5 +-
 tests/40components.tests                      |   7 +-
 .../proc/engine/buffer-metadata-test.cpp      |   2 +-
 tests/lib/simple-allocator-test.cpp           | 160 +++++++++++
 10 files changed, 440 insertions(+), 12 deletions(-)
 create mode 100644 src/lib/simple-allocator.hpp
 create mode 100644 tests/lib/simple-allocator-test.cpp

diff --git a/src/lib/result.hpp b/src/lib/result.hpp
index daf153a49..2f42c7f64 100644
--- a/src/lib/result.hpp
+++ b/src/lib/result.hpp
@@ -24,9 +24,9 @@
 /** @file result.hpp
  ** Intermediary value object to represent the result of an operation.
  ** This operation might have produced a value result or failed with an exception.
- ** Typically, the Result token used \em inline -- immediately either invoking one
- ** of the member function or employing the built-in result type conversion. It
- ** will be copyable iff the result value is copyable. There is an implicit
+ ** Typically, the Result token is used \em inline -- immediately either invoking
+ ** one of the member function or employing the built-in result type conversion.
+ ** It will be copyable iff the result value is copyable. There is an implicit
  ** valid or failure state, which can be tested. Any attempt to get the value
  ** of an invalid result token will cause in an exception to be thrown.
  ** 
diff --git a/src/lib/scoped-holder-transfer.hpp b/src/lib/scoped-holder-transfer.hpp
index e1ee6d5c9..58508f393 100644
--- a/src/lib/scoped-holder-transfer.hpp
+++ b/src/lib/scoped-holder-transfer.hpp
@@ -105,8 +105,8 @@ namespace lib {
       pointer       address(reference r)            const { return par_.address(r); }
       const_pointer address(const_reference cr)     const { return par_.address(cr); }
       pointer       allocate(size_type n, const void *p=0){ return par_.allocate(n,p); }
-      void          deallocate(pointer p, size_type n)    { return par_.deallocate(p,n); }
-      void          destroy(pointer p)                    { return par_.destroy(p); }
+      void          deallocate(pointer p, size_type n)    {        par_.deallocate(p,n); }
+      void          destroy(pointer p)                    {        par_.destroy(p); }
       
       
       void 
diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp
new file mode 100644
index 000000000..71b0438fa
--- /dev/null
+++ b/src/lib/simple-allocator.hpp
@@ -0,0 +1,262 @@
+/*
+  SIMPLE-ALLOCATOR.hpp  -  frontend for plain explicit allocations 
+
+  Copyright (C)         Lumiera.org
+    2011,               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 simple-allocator.hpp
+ ** Frontend and marker interface for allocating small objects explicitly.
+ ** Contrary to the TypedAllocationManager, the SimpleAllocator doesn't provide any
+ ** ref-counting or tracking facilities, nor does he support bulk de-allocation.
+ ** Each object needs to be allocated and released by explicit call.
+ ** The advantage over using std::allocator  directly is the shortcut for (placement) construction,
+ ** and -- of course -- the ability to exchange the memory model at one central location.
+ ** 
+ ** SimpleAllocator instances will be defined for a specific collection of types; for each of those
+ ** types, there will be an embedded dedicated custom allocator (currently as of 9/2011, just
+ ** implemented as std::allocator). Objects of these preconfigured types can be constructed
+ ** and destroyed through this allocator instance. Each call needs to be done explicitly, with
+ ** the precise, concrete type to be created or destroyed. This is especially important for
+ ** the releasing of objects: there is \em no support for any kind of virtual destruction.
+ ** 
+ ** @see engine::BufferMetadata
+ ** @see TypedAllocationManager (another more elaborate custom allocation scheme)
+ **
+ */
+
+
+
+#ifndef LIB_SIMPLE_ALLOCATOR_H
+#define LIB_SIMPLE_ALLOCATOR_H
+
+//#include "pre.hpp"
+#include "lib/error.hpp"
+#include "lib/meta/generator.hpp"
+#include "lib/format.hpp"
+//#include "lib/typed-counter.hpp"
+#include "include/logging.h"
+
+
+//#include 
+#include 
+#include 
+
+
+
+namespace lib {
+  
+//  using std::tr1::shared_ptr;
+  using lumiera::typelist::InstantiateForEach;
+  using lumiera::typelist::Types;
+  
+  namespace {
+    using lumiera::typelist::Node;
+    using lumiera::typelist::NullType;
+    
+    template
+    struct IsInList
+      {
+        enum{ value = false };
+      };
+    
+    template
+    struct IsInList, TY>
+      {
+        enum{ value = true };
+      };
+    
+    template
+    struct IsInList, TY>
+      {
+        enum{ value = IsInList::value };
+      };
+    
+  }
+  
+  
+  template
+  class CustomAllocator
+    : public std::allocator
+    {
+      
+    };
+  
+    
+  
+  /**
+   * Frontend for explicit allocations, using a custom allocator.
+   * This template is to be instantiated for the collection of types
+   * later to be allocated through this custom memory manager/model.
+   * It provides convenience shortcuts for placement-construction
+   * and releasing of target objects.
+   * 
+   * @todo currently (as of 8/09) the low-level pooled allocator
+   *       isn't implemented; instead we do just heap allocations.
+   *       see Ticket #835
+   */
+  template
+  class SimpleAllocator
+    : InstantiateForEach< typename TYPES::List     // for each of those types...         
+                        , CustomAllocator         //  ...mix in the custom allocator 
+                        >
+    {
+      
+      /** forward plain memory allocation */
+      template
+      XX *
+      allocateSlot ()
+        {
+          TRACE (memory, "allocate %s", util::tyStr().c_str());
+          return CustomAllocator::allocate (1);
+        }
+      
+      template
+      void
+      releaseSlot (XX* entry)
+        {
+          TRACE (memory, "release %s", util::tyStr().c_str());
+          CustomAllocator::deallocate (entry, 1);
+        }
+      
+      
+      template
+      void
+      ___assertSupportedType()
+        {
+          typedef typename TYPES::List PreconfiguredTypes;
+          typedef IsInList IsPreconfiguredType;
+          
+          BOOST_STATIC_ASSERT (IsPreconfiguredType::value);
+        }
+     
+        
+      
+      
+      public: /* ==== build objects with managed allocation ==== */
+      
+#define _EXCEPTION_SAFE_INVOKE(_CTOR_)                      \
+                                                             \
+          ___assertSupportedType();                       \
+          XX* storage = allocateSlot();                    \
+          try                                                   \
+            {                                                    \
+              return (new(storage) _CTOR_ );                     \
+            }                                                    \
+          catch(...)                                             \
+            {                                                    \
+              releaseSlot(storage);                          \
+              throw;                                             \
+            }
+      
+      template< class XX>
+      XX*                                                                              //_____________________
+      create ()                                                                       ///< invoke default ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX() )
+        }
+      
+      
+      template< class XX, typename P1>
+      XX*                                                                              //___________________
+      create (P1& p1)                                                                 ///< invoke 1-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1) )
+        }
+      
+      
+      template< class XX
+              , typename P1
+              , typename P2
+              >
+      XX*                                                                              //___________________
+      create (P1& p1, P2& p2)                                                         ///< invoke 2-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1,p2) )
+        }
+      
+      
+      template< class XX
+              , typename P1
+              , typename P2
+              , typename P3
+              >
+      XX*                                                                              //___________________
+      create (P1& p1, P2& p2, P3& p3)                                                 ///< invoke 3-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3) )
+        }
+      
+      
+      template< class XX
+              , typename P1
+              , typename P2
+              , typename P3
+              , typename P4
+              >
+      XX*                                                                              //___________________
+      create (P1& p1, P2& p2, P3& p3, P4& p4)                                         ///< invoke 4-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4) )
+        }
+      
+      
+      template< class XX
+              , typename P1
+              , typename P2
+              , typename P3
+              , typename P4
+              , typename P5
+              >
+      XX*                                                                              //___________________
+      create (P1& p1, P2& p2, P3& p3, P4& p4, P5& p5)                                 ///< invoke 5-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4,p5) )
+        }
+      
+#undef _EXCEPTION_SAFE_INVOKE
+      
+      
+      
+      template
+      void
+      destroy (XX* entry)
+        {
+          if (!entry) return;
+          ___assertSupportedType();
+          try
+            {
+              entry->~XX();
+            }
+          catch(...)
+            {
+              lumiera_err errorID = lumiera_error();
+              WARN (common_dbg, "dtor of %s failed: %s", util::tyStr(entry).c_str()
+                                                       , errorID );
+            }
+          releaseSlot (entry);
+        }
+      
+    };
+  
+  
+  
+
+} // namespace lib
+#endif
diff --git a/src/lib/typed-allocation-manager.hpp b/src/lib/typed-allocation-manager.hpp
index 188f120dc..d1a04b19e 100644
--- a/src/lib/typed-allocation-manager.hpp
+++ b/src/lib/typed-allocation-manager.hpp
@@ -63,8 +63,8 @@
 
 
 
-#ifndef CONTROL_TYPED_ALLOCATION_MANAGER_H
-#define CONTROL_TYPED_ALLOCATION_MANAGER_H
+#ifndef LIB_TYPED_ALLOCATION_MANAGER_H
+#define LIB_TYPED_ALLOCATION_MANAGER_H
 
 //#include "pre.hpp"
 #include "lib/error.hpp"
diff --git a/src/proc/control/command-argument-holder.hpp b/src/proc/control/command-argument-holder.hpp
index e71a60a10..d40fd0433 100644
--- a/src/proc/control/command-argument-holder.hpp
+++ b/src/proc/control/command-argument-holder.hpp
@@ -54,7 +54,6 @@
 
 namespace control {
   
-  using lib::TypedAllocationManager;
   using lib::InPlaceBuffer;
   using std::string;
   
diff --git a/src/proc/control/command-impl.hpp b/src/proc/control/command-impl.hpp
index 7d3c18536..a25d92fad 100644
--- a/src/proc/control/command-impl.hpp
+++ b/src/proc/control/command-impl.hpp
@@ -57,7 +57,6 @@
 
 namespace control {
   
-  using lib::TypedAllocationManager;
   using std::tr1::function;
   using std::tr1::shared_ptr;
   
diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index c71359313..ae59f93f6 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -104,8 +104,11 @@ namespace engine {
   class Metadata
     {
     public:
-      struct Entry
+      class Entry
         {
+          
+        
+        public:
           BufferState state()  const;
           HashVal parentKey()  const;
           Entry& mark (BufferState newState);
diff --git a/tests/40components.tests b/tests/40components.tests
index a81b77d3b..9dfaffd74 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -1136,7 +1136,12 @@ return: 0
 END
 
 
-TEST "TypedAllocationManager" TypedAllocationManager_test <
+
+  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/test/run.hpp"
+#include "lib/simple-allocator.hpp"
+#include "lib/util.hpp"
+
+
+//#include 
+#include 
+#include 
+
+
+namespace lib {
+namespace test{
+  
+  
+  using util::isSameObject;
+//  using std::tr1::shared_ptr;
+  using std::string;
+  using std::rand;
+  
+  
+  
+  namespace { // test data and helpers...
+  
+    long checksum_ = 0;
+    
+    /**
+     * Yet-another ctor/dtor-tracking test dummy object....
+     */
+    template
+    class DummyObj
+      {
+        char crap_[siz];
+        
+      public:
+        DummyObj()
+          {
+            REQUIRE (siz);
+            for (uint i=0; i,DummyObj<23>,string> > TestAllocator;
+  }
+  
+  
+  
+  /***************************************************************************************
+   * @test cover the basic operations of a custom allocator, delegating to mpool.
+   *       The SimpleAllocator doesn't provide any ref-counting or tracking facilities,
+   *       nor does he support bulk de-allocation. The advantage over using std::allocator
+   *       directly is the shortcut for (placement) construction, and -- of course -- the
+   *       ability to exchange the memory model at one central location.
+   * 
+   * @todo as of 9/11 we do heap allocation, but we should use mpool --   see also Ticket #835
+   *        
+   * @see engine::BufferMetadata
+   * @see TypedAllocationManager_test
+   */
+  class SimpleAllocator_test : public Test
+    {
+      
+      virtual void
+      run (Arg) 
+        {
+          CHECK (0 == checksum_);
+          
+          TestAllocator allocator;
+          
+          typedef string *       PS;
+          typedef DummyObj<1> *  PD1;
+          typedef DummyObj<23> * PD23;
+          CHECK (sizeof(DummyObj<1>) != sizeof(DummyObj<23>));
+          
+          PD1  pD11 = allocator.create >();
+          PD1  pD12 = allocator.create >();
+          PD23 pD21 = allocator.create >();
+          PD23 pD22 = allocator.create >();
+          PS   pS11 = allocator.create ("Lumiera");
+          PS   pS12 = allocator.create ("the paradox");
+          
+          CHECK (pD11);
+          CHECK (pD12);
+          CHECK (pD21);
+          CHECK (pD22);
+          CHECK (pS11);
+          CHECK (pS12);
+          CHECK (!isSameObject (*pD11, *pD12));
+          CHECK (!isSameObject (*pD11, *pD21));
+          CHECK (!isSameObject (*pD11, *pD22));
+          CHECK (!isSameObject (*pD11, *pS11));
+          CHECK (!isSameObject (*pD11, *pS12));
+          CHECK (!isSameObject (*pD12, *pD21));
+          CHECK (!isSameObject (*pD12, *pD22));
+          CHECK (!isSameObject (*pD12, *pS11));
+          CHECK (!isSameObject (*pD12, *pS12));
+          CHECK (!isSameObject (*pD21, *pD22));
+          CHECK (!isSameObject (*pD21, *pS11));
+          CHECK (!isSameObject (*pD21, *pS12));
+          CHECK (!isSameObject (*pD22, *pS11));
+          CHECK (!isSameObject (*pD22, *pS12));
+          CHECK (!isSameObject (*pS11, *pS12));
+          
+          CHECK (*pS11 == "Lumiera");
+          CHECK (*pS12 == "the paradox");
+          
+          PD23 pDxx = allocator.create > (*pD21);
+          PS   pSxx = allocator.create        (*pS12);
+          
+          CHECK (*pS12 == *pSxx);
+          CHECK (!isSameObject (*pS12, *pSxx));
+          
+          allocator.destroy (pD11);
+          allocator.destroy (pD12);
+          allocator.destroy (pD21);
+          allocator.destroy (pD22);
+          allocator.destroy (pS11);
+          allocator.destroy (pS12);
+          allocator.destroy (pDxx);
+          allocator.destroy (pSxx);
+          
+          CHECK (0 == checksum_);
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (SimpleAllocator_test, "unit common");
+  
+  
+}} // namespace lib::test

From 057f32e15b67951dd7f41374af3e1b1a771857a9 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Sep 2011 15:40:16 +0200
Subject: [PATCH 082/296] rename the typelist-manipulation header

---
 src/lib/meta/generator-combinations.hpp  |   2 +-
 src/lib/meta/tuple.hpp                   |   2 +-
 src/lib/meta/typelist-manip.hpp          | 366 +++++++++++++++++++++++
 src/lib/meta/typeseq-util.hpp            |   2 +-
 src/lib/variant.hpp                      |   2 +-
 src/proc/control/command-def.hpp         |   2 +-
 src/proc/control/command-signature.hpp   |   5 +-
 src/proc/engine/nodewiring.cpp           |   2 +-
 src/proc/mobject/output-designation.hpp  |   2 +-
 tests/lib/meta/config-flags-test.cpp     |   2 +-
 tests/lib/meta/function-closure-test.cpp |   2 +-
 tests/lib/meta/typelist-manip-test.cpp   |   2 +-
 tests/lib/meta/typeseq-manip-test.cpp    |   2 +-
 13 files changed, 378 insertions(+), 15 deletions(-)
 create mode 100644 src/lib/meta/typelist-manip.hpp

diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp
index 28790ec01..3cc3a0946 100644
--- a/src/lib/meta/generator-combinations.hpp
+++ b/src/lib/meta/generator-combinations.hpp
@@ -37,7 +37,7 @@
 #define LUMIERA_META_GENERATOR_COMBINATIONS_H
 
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/generator.hpp"
 
 
diff --git a/src/lib/meta/tuple.hpp b/src/lib/meta/tuple.hpp
index 38d5def29..84559216a 100644
--- a/src/lib/meta/tuple.hpp
+++ b/src/lib/meta/tuple.hpp
@@ -48,7 +48,7 @@
 #define LUMIERA_META_TUPLE_H
 
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/typeseq-util.hpp"
 #include "lib/meta/util.hpp"
 
diff --git a/src/lib/meta/typelist-manip.hpp b/src/lib/meta/typelist-manip.hpp
new file mode 100644
index 000000000..428c23f97
--- /dev/null
+++ b/src/lib/meta/typelist-manip.hpp
@@ -0,0 +1,366 @@
+/*
+  TYPELIST-UTIL.hpp  -  Utils for working with lists-of-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.
+
+*/
+
+
+/** @file typelist-util.hpp
+ ** Metaprogramming: Helpers for manipulating lists-of-types. 
+ ** Sometimes, we use metaprogramming to generate a variation of concrete
+ ** implementations by combining some basic building blocks. Typically, there
+ ** is a number of similar, but not suitably related types involved. We want to
+ ** process those types using a common scheme, without being forced to squeeze
+ ** all those types into a artificial inheritance relationship. Now, generating
+ ** some kind of common factory or adapter, while mixing in pieces of code tailored
+ ** specifically to the individual types, allows still to build a common processing
+ ** in such situations.
+ ** 
+ ** The facilities in this header provide the basics of simple functional list
+ ** processing (mostly with tail recursion). Usually, there is one template parameter
+ ** TYPES, which accepts a \em Type-list. The result of the processing step is then
+ ** accessible as an embedded typedef named \c List . Here, all of the 'processing'
+ ** to calculate this result is performed by the compiler, as a side-effect of
+ ** figuring out the resulting concrete type. At run time, in the generated
+ ** code, typically the resulting classes are empty, maybe just
+ ** exposing a specifically built-up function. 
+ ** 
+ ** @see generator.hpp
+ ** @see typelist-manip-test.cpp
+ ** @see TimeControl_test usage example
+ ** @see typelist.hpp
+ ** 
+ */
+
+
+#ifndef LUMIERA_META_TYPELIST_UTIL_H
+#define LUMIERA_META_TYPELIST_UTIL_H
+
+
+  
+#include "lib/meta/typelist.hpp"
+
+namespace lumiera {
+namespace typelist{
+    
+    
+    /**
+     * Metafunction counting the number of Types in the collection
+     * @return an embedded constant \c value holding the result
+     */
+    template
+    struct count;
+    template<>
+    struct count
+      {
+        enum{ value = 0 };
+      };
+    template
+    struct count >
+      {
+        enum{ value = 1 + count::value };
+      };
+    
+    /**
+     * Metafunction " max( sizeof(T) ) for T in TYPES "
+     */
+    template
+    struct maxSize;
+    template<>
+    struct maxSize
+      {
+        enum{ value = 0 };
+      };
+    template
+    struct maxSize >
+      {
+        enum{ thisval = sizeof(TY)
+            , nextval = maxSize::value
+            , value   = nextval > thisval?  nextval:thisval
+            };
+      };
+    
+    
+    /** pick the n-th element from a typelist */
+    template
+    struct Pick
+      {
+        typedef NullType Type;  
+      };
+    template
+    struct Pick, 0>
+      {
+        typedef TY Type;  
+      };
+    template
+    struct Pick, i>
+      {
+        typedef typename Pick::Type Type;  
+      };
+    
+    
+    
+    
+    /** apply a transformation (template) to each type in the list */
+    template class _TRANS_>
+    struct Apply                           { typedef TY List; };
+    
+    template< class TY, class TYPES
+            , template class _TRANS_
+            >
+    struct Apply, _TRANS_ > { typedef Node< typename _TRANS_::Type
+                                                         , typename Apply::List
+                                                         > List; };
+    
+    
+    /** conditional node: skip an element based on evaluating a predicate */
+    template
+    struct CondNode                        { typedef TAIL  Next; };
+    
+    template
+    struct CondNode         { typedef Node  Next; };
+    
+    /** filter away those types which don't fulfil a predicate metafunction */
+    template< class TYPES 
+            , template class _P_    ///< a template providing a boolean member \c ::value
+            >
+    struct Filter;
+    
+    template class _P_>
+    struct Filter            { typedef NullType  List; };
+    
+    template< class TY, class TYPES
+            , template class _P_
+            >
+    struct Filter,_P_>      { typedef typename CondNode< _P_::value
+                                                                      , TY
+                                                                      , typename Filter::List
+                                                                      >::Next      
+                                                                      List; };
+    
+    
+    /** append lists-of-types */
+    template
+    struct Append                          { typedef Node::List>  List; };
+    
+    template< class TY, class TYPES
+            , class TAIL
+            >
+    struct Append, TAIL>    { typedef Node::List>  List; };
+    
+    template
+    struct Append >   { typedef Node   List; };
+    
+    template
+    struct Append, NullType>    { typedef Node   List; };
+    
+    template
+    struct Append            { typedef Node   List; };
+    
+    template
+    struct Append            { typedef Node   List; };
+    
+    template<>
+    struct Append       { typedef NullType             List; };
+    
+    
+    
+    
+    /** access the last list element */
+    template
+    struct SplitLast;
+    
+    template<>
+    struct SplitLast             { typedef NullType Type;
+                                             typedef NullType List; };
+    template
+    struct SplitLast >   { typedef TY       Type;
+                                             typedef NullType List; };
+    
+    template
+    struct SplitLast >      { typedef typename SplitLast::Type Type;
+                                             typedef typename Append< TY,
+                                                                      typename SplitLast::List
+                                                                    >::List 
+                                                                    List; };
+    
+    
+    
+    /** 
+     * splice a typelist like an overlay
+     * into an base typelist, starting at given index.
+     * @return either the combined (spliced) List, or
+     *         the Front/Back part before or after the Overlay
+     * @note using a NullType as OVERLAY allows to extract
+     *         an arbitrary Front/Back part of the list
+     */
+    template
+    struct Splice;
+    
+    template
+    struct Splice, OVERLAY, i>  { typedef Node::List>  List;
+                                             typedef Node::Front> Front;
+                                             typedef         typename Splice::Back   Back; };
+    
+    template
+    struct Splice,Node,0> { typedef Node::List>          List;
+                                             typedef NullType                                          Front;
+                                             typedef         typename Splice::Back           Back; };
+    
+    template
+    struct Splice, NullType, 0> { typedef Node List;
+                                             typedef NullType    Front;
+                                             typedef Node Back; };
+    
+    template
+    struct Splice         { typedef NullType    List;
+                                             typedef NullType    Front;
+                                             typedef NullType    Back; };
+    
+    
+    
+    
+    /**
+     * Allows to access various parts of a given typelist:
+     * Start and End, Prefix and Tail..
+     */
+    template
+    struct Dissect;
+    
+    template
+    struct Dissect >
+      {
+        typedef Node                  List;  ///< the complete list
+        typedef T                              Head;  ///< first element
+        typedef Node               First; ///< a list containing the first element
+        typedef TYPES                          Tail;  ///< remainder of the list starting with the second elm.
+        typedef typename SplitLast::List Prefix;///< all of the list, up to but excluding the last element
+        typedef typename SplitLast::Type End;   ///< the last element
+        typedef Node             Last;  ///< a list containing the last element
+      };
+    
+    template<>
+    struct Dissect
+      {
+        typedef NullType                       List;
+        typedef NullType                       Head;
+        typedef NullType                       First;
+        typedef NullType                       Tail;
+        typedef NullType                       Prefix;
+        typedef NullType                       End;
+        typedef NullType                       Last;
+      };
+    
+    
+    
+    
+    /** 
+     * prefix each of the elements,
+     * yielding a list-of lists-of-types
+     */
+    template
+    struct PrefixAll                       { typedef Node< typename Append::List, NullType>  List; };
+    
+    template
+    struct PrefixAll          { typedef NullType  List; };
+    
+    template
+    struct PrefixAll          { typedef Node< typename Append::List, NullType>  List; };
+    
+    template< class T
+            , class TY, class TYPES
+            >
+    struct PrefixAll >   { typedef Node< typename Append::List
+                                                         , typename PrefixAll::List
+                                                         >     List; };
+    
+    
+    
+    
+    /**
+     * build a list-of lists, where each element of the first arg list
+     * gets in turn prepended to all elements of the second arg list.
+     * Can be used to build all possible combinations from two
+     * sources, i.e. the Cartesian product.
+     */
+    template
+    struct Distribute                      { typedef typename PrefixAll::List  List; };
+    
+    template
+    struct Distribute         { typedef NullType List; };
+    
+    template< class TY, class TYPES
+            , class TAIL
+            >
+    struct Distribute,TAIL> { typedef typename Append< typename PrefixAll::List
+                                                                    , typename Distribute::List
+                                                                    >::List                    
+                                                                    List; };
+    
+    
+    
+    /** 
+     * build all possible combinations, based on a enumeration of the basic cases.
+     * For each of the types in the argument list, an "enumeration generator" template is invoked,
+     * yielding a list of the possible base cases. These base cases are then combined with all the
+     * combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered"
+     * means that the base cases of the n-th element will appear in the n-th position of the
+     * resulting lists,
+     * 
+     * For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a
+     * list-of-lists, featuring all possibilities to combine these distinct toggles. 
+     */
+    template< class X
+            , template class _ENUM_>
+    struct Combine                         { typedef typename Distribute< typename _ENUM_::List
+                                                                        , Node
+                                                                        >::List  List; };
+    template< template class _ENUM_>
+    struct Combine      { typedef NodeNull                    List; };
+    
+    template< class TY, class TYPES
+            , template class _ENUM_>
+    struct Combine,_ENUM_>  { typedef typename Distribute< typename _ENUM_::List
+                                                                        , typename Combine::List
+                                                                        >::List  List; };
+    
+    /** enumeration generator for the Combine metafunction,
+     *  yielding an "on" and "off" case
+     */
+    template
+    struct FlagOnOff
+      { 
+        typedef Node >  List;
+      };
+    
+    
+    /** generate all possible on-off combinations of the given flags */
+    template
+    struct CombineFlags
+      { 
+        typedef typename Combine::List  List; 
+      };
+    
+    
+    
+    
+}} // namespace lumiera::typelist
+#endif
diff --git a/src/lib/meta/typeseq-util.hpp b/src/lib/meta/typeseq-util.hpp
index 398d09d53..07ecbffa6 100644
--- a/src/lib/meta/typeseq-util.hpp
+++ b/src/lib/meta/typeseq-util.hpp
@@ -45,7 +45,7 @@
 #define LUMIERA_META_TYPESEQ_UTIL_H
 
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/util.hpp"
 
 
diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp
index b4eef9b76..7b30ba63c 100644
--- a/src/lib/variant.hpp
+++ b/src/lib/variant.hpp
@@ -45,7 +45,7 @@
 #define LIB_VARIANT_H
 
 
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/generator.hpp"
 
 #include 
diff --git a/src/proc/control/command-def.hpp b/src/proc/control/command-def.hpp
index b3e777c4c..07490ce0b 100644
--- a/src/proc/control/command-def.hpp
+++ b/src/proc/control/command-def.hpp
@@ -67,7 +67,7 @@
 #include "lib/bool-checkable.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/tuple.hpp"
 #include "lib/util.hpp"
 
diff --git a/src/proc/control/command-signature.hpp b/src/proc/control/command-signature.hpp
index 3c9df666f..52e4dcf19 100644
--- a/src/proc/control/command-signature.hpp
+++ b/src/proc/control/command-signature.hpp
@@ -47,14 +47,11 @@
 #define CONTROL_COMMAND_SIGNATURE_H
 
 //#include "pre.hpp"
-//#include "lib/symbol.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/typeseq-util.hpp"
-//#include "lib/meta/tuple.hpp"
 
-//#include 
 #include 
 
 
diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp
index f1e989816..9be5dd541 100644
--- a/src/proc/engine/nodewiring.cpp
+++ b/src/proc/engine/nodewiring.cpp
@@ -26,7 +26,7 @@
 #include "proc/engine/nodeoperation.hpp"
 #include "proc/engine/nodewiring-config.hpp"
 
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 
 
 namespace engine {
diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp
index 93f98f518..bfd47b8a5 100644
--- a/src/proc/mobject/output-designation.hpp
+++ b/src/proc/mobject/output-designation.hpp
@@ -26,7 +26,7 @@
 
 #include "proc/asset/pipe.hpp"
 #include "lib/opaque-holder.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 
 extern "C" {
 #include "lib/luid.h"
diff --git a/tests/lib/meta/config-flags-test.cpp b/tests/lib/meta/config-flags-test.cpp
index 57a5621d1..757f84d39 100644
--- a/tests/lib/meta/config-flags-test.cpp
+++ b/tests/lib/meta/config-flags-test.cpp
@@ -40,7 +40,7 @@
 #include "lib/test/run.hpp"
 #include "lib/meta/util.hpp"
 #include "lib/meta/generator.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/configflags.hpp"
 #include "meta/typelist-diagnostics.hpp"
 #include "proc/engine/nodewiring-config.hpp"
diff --git a/tests/lib/meta/function-closure-test.cpp b/tests/lib/meta/function-closure-test.cpp
index e024ff808..4d2eb2a06 100644
--- a/tests/lib/meta/function-closure-test.cpp
+++ b/tests/lib/meta/function-closure-test.cpp
@@ -38,7 +38,7 @@
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/meta/function-closure.hpp"
 #include "meta/typelist-diagnostics.hpp"
diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp
index a14daef90..57f88d1dd 100644
--- a/tests/lib/meta/typelist-manip-test.cpp
+++ b/tests/lib/meta/typelist-manip-test.cpp
@@ -40,7 +40,7 @@
 
 #include "lib/test/run.hpp"
 #include "lib/meta/generator.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "meta/typelist-diagnostics.hpp"
 
 #include 
diff --git a/tests/lib/meta/typeseq-manip-test.cpp b/tests/lib/meta/typeseq-manip-test.cpp
index 6efffd272..3445d2217 100644
--- a/tests/lib/meta/typeseq-manip-test.cpp
+++ b/tests/lib/meta/typeseq-manip-test.cpp
@@ -39,7 +39,7 @@
 
 #include "lib/test/run.hpp"
 #include "lib/meta/typeseq-util.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "meta/typelist-diagnostics.hpp"
 
 #include 

From 5350ef6dbe7512ca85473a9fac2181d0b8b373c5 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Sep 2011 17:06:48 +0200
Subject: [PATCH 083/296] split off and test-cover simple typelist utils

these simple utils (length of typelist, maximum size
containment test) are mostly not used in conjunction
with the more elaborate typelist manipulatino utils.
Moreover, we lacked a dedicated unit test
---
 src/lib/meta/tuple.hpp                 |   2 +-
 src/lib/meta/typelist-manip.hpp        |  55 +----
 src/lib/meta/typelist-util.hpp         | 307 +++----------------------
 src/lib/meta/typelist.hpp              |  20 +-
 src/lib/simple-allocator.hpp           |  29 +--
 src/lib/variant.hpp                    |   2 +-
 src/proc/control/command-signature.hpp |  23 +-
 tests/40components.tests               |   5 +
 tests/lib/meta/typelist-util-test.cpp  |  83 +++++++
 9 files changed, 166 insertions(+), 360 deletions(-)
 create mode 100644 tests/lib/meta/typelist-util-test.cpp

diff --git a/src/lib/meta/tuple.hpp b/src/lib/meta/tuple.hpp
index 84559216a..38d5def29 100644
--- a/src/lib/meta/tuple.hpp
+++ b/src/lib/meta/tuple.hpp
@@ -48,7 +48,7 @@
 #define LUMIERA_META_TUPLE_H
 
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-manip.hpp"
+#include "lib/meta/typelist-util.hpp"
 #include "lib/meta/typeseq-util.hpp"
 #include "lib/meta/util.hpp"
 
diff --git a/src/lib/meta/typelist-manip.hpp b/src/lib/meta/typelist-manip.hpp
index 428c23f97..b0d789a76 100644
--- a/src/lib/meta/typelist-manip.hpp
+++ b/src/lib/meta/typelist-manip.hpp
@@ -1,5 +1,5 @@
 /*
-  TYPELIST-UTIL.hpp  -  Utils for working with lists-of-types
+  TYPELIST-MANIP.hpp  -  Utils for working with lists-of-types
 
   Copyright (C)         Lumiera.org
     2008,               Hermann Vosseler 
@@ -21,8 +21,8 @@
 */
 
 
-/** @file typelist-util.hpp
- ** Metaprogramming: Helpers for manipulating lists-of-types. 
+/** @file typelist-manip.hpp
+ ** Metaprogramming: Helpers for manipulating lists-of-types.
  ** Sometimes, we use metaprogramming to generate a variation of concrete
  ** implementations by combining some basic building blocks. Typically, there
  ** is a number of similar, but not suitably related types involved. We want to
@@ -49,54 +49,17 @@
  */
 
 
-#ifndef LUMIERA_META_TYPELIST_UTIL_H
-#define LUMIERA_META_TYPELIST_UTIL_H
+#ifndef LUMIERA_META_TYPELIST_MANIP_H
+#define LUMIERA_META_TYPELIST_MANIP_H
+
 
 
-  
 #include "lib/meta/typelist.hpp"
 
 namespace lumiera {
 namespace typelist{
     
     
-    /**
-     * Metafunction counting the number of Types in the collection
-     * @return an embedded constant \c value holding the result
-     */
-    template
-    struct count;
-    template<>
-    struct count
-      {
-        enum{ value = 0 };
-      };
-    template
-    struct count >
-      {
-        enum{ value = 1 + count::value };
-      };
-    
-    /**
-     * Metafunction " max( sizeof(T) ) for T in TYPES "
-     */
-    template
-    struct maxSize;
-    template<>
-    struct maxSize
-      {
-        enum{ value = 0 };
-      };
-    template
-    struct maxSize >
-      {
-        enum{ thisval = sizeof(TY)
-            , nextval = maxSize::value
-            , value   = nextval > thisval?  nextval:thisval
-            };
-      };
-    
-    
     /** pick the n-th element from a typelist */
     template
     struct Pick
@@ -111,7 +74,7 @@ namespace typelist{
     template
     struct Pick, i>
       {
-        typedef typename Pick::Type Type;  
+        typedef typename Pick::Type Type;
       };
     
     
@@ -240,7 +203,7 @@ namespace typelist{
     
     /**
      * Allows to access various parts of a given typelist:
-     * Start and End, Prefix and Tail..
+     * Start and End, Prefix and Tail...
      */
     template
     struct Dissect;
@@ -356,7 +319,7 @@ namespace typelist{
     template
     struct CombineFlags
       { 
-        typedef typename Combine::List  List; 
+        typedef typename Combine::List  List;
       };
     
     
diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp
index 428c23f97..c699855ad 100644
--- a/src/lib/meta/typelist-util.hpp
+++ b/src/lib/meta/typelist-util.hpp
@@ -1,5 +1,5 @@
 /*
-  TYPELIST-UTIL.hpp  -  Utils for working with lists-of-types
+  TYPELIST-UTIL.hpp  -  simple helpers for working with lists-of-types
 
   Copyright (C)         Lumiera.org
     2008,               Hermann Vosseler 
@@ -22,28 +22,21 @@
 
 
 /** @file typelist-util.hpp
- ** Metaprogramming: Helpers for manipulating lists-of-types. 
- ** Sometimes, we use metaprogramming to generate a variation of concrete
- ** implementations by combining some basic building blocks. Typically, there
- ** is a number of similar, but not suitably related types involved. We want to
- ** process those types using a common scheme, without being forced to squeeze
- ** all those types into a artificial inheritance relationship. Now, generating
- ** some kind of common factory or adapter, while mixing in pieces of code tailored
- ** specifically to the individual types, allows still to build a common processing
- ** in such situations.
+ ** Metaprogramming: simple helpers for working with lists-of-types. 
+ ** This header provides some very basic "meta functions" for extracting
+ ** some informations from a list-of-types. In Lumiera, we use template
+ ** metaprogramming and especially such lists-of-types, whenever we build
+ ** some common implementation backbone, without being able to subsume all
+ ** participating types (classes) into a single inheritance hierarchy.
  ** 
- ** The facilities in this header provide the basics of simple functional list
- ** processing (mostly with tail recursion). Usually, there is one template parameter
- ** TYPES, which accepts a \em Type-list. The result of the processing step is then
- ** accessible as an embedded typedef named \c List . Here, all of the 'processing'
- ** to calculate this result is performed by the compiler, as a side-effect of
- ** figuring out the resulting concrete type. At run time, in the generated
- ** code, typically the resulting classes are empty, maybe just
- ** exposing a specifically built-up function. 
+ ** The "meta functions" defined here are templates; to access the "result" of
+ ** such a meta function, we instantiate the template and then access one of the
+ ** embedded constant definitions (usually the enum constant named \c value)
  ** 
  ** @see generator.hpp
- ** @see typelist-manip-test.cpp
- ** @see TimeControl_test usage example
+ ** @see TypelistUtil_test
+ ** @see lib::SimpleAllocator usage example (for isInList)
+ ** @see TypelistManip_test
  ** @see typelist.hpp
  ** 
  */
@@ -53,7 +46,7 @@
 #define LUMIERA_META_TYPELIST_UTIL_H
 
 
-  
+
 #include "lib/meta/typelist.hpp"
 
 namespace lumiera {
@@ -77,6 +70,7 @@ namespace typelist{
         enum{ value = 1 + count::value };
       };
     
+    
     /**
      * Metafunction " max( sizeof(T) ) for T in TYPES "
      */
@@ -97,268 +91,35 @@ namespace typelist{
       };
     
     
-    /** pick the n-th element from a typelist */
-    template
-    struct Pick
-      {
-        typedef NullType Type;  
-      };
-    template
-    struct Pick, 0>
-      {
-        typedef TY Type;  
-      };
-    template
-    struct Pick, i>
-      {
-        typedef typename Pick::Type Type;  
-      };
-    
-    
-    
-    
-    /** apply a transformation (template) to each type in the list */
-    template class _TRANS_>
-    struct Apply                           { typedef TY List; };
-    
-    template< class TY, class TYPES
-            , template class _TRANS_
-            >
-    struct Apply, _TRANS_ > { typedef Node< typename _TRANS_::Type
-                                                         , typename Apply::List
-                                                         > List; };
-    
-    
-    /** conditional node: skip an element based on evaluating a predicate */
-    template
-    struct CondNode                        { typedef TAIL  Next; };
-    
-    template
-    struct CondNode         { typedef Node  Next; };
-    
-    /** filter away those types which don't fulfil a predicate metafunction */
-    template< class TYPES 
-            , template class _P_    ///< a template providing a boolean member \c ::value
-            >
-    struct Filter;
-    
-    template class _P_>
-    struct Filter            { typedef NullType  List; };
-    
-    template< class TY, class TYPES
-            , template class _P_
-            >
-    struct Filter,_P_>      { typedef typename CondNode< _P_::value
-                                                                      , TY
-                                                                      , typename Filter::List
-                                                                      >::Next      
-                                                                      List; };
-    
-    
-    /** append lists-of-types */
-    template
-    struct Append                          { typedef Node::List>  List; };
-    
-    template< class TY, class TYPES
-            , class TAIL
-            >
-    struct Append, TAIL>    { typedef Node::List>  List; };
-    
-    template
-    struct Append >   { typedef Node   List; };
-    
-    template
-    struct Append, NullType>    { typedef Node   List; };
-    
-    template
-    struct Append            { typedef Node   List; };
-    
-    template
-    struct Append            { typedef Node   List; };
-    
-    template<>
-    struct Append       { typedef NullType             List; };
-    
-    
-    
-    
-    /** access the last list element */
-    template
-    struct SplitLast;
-    
-    template<>
-    struct SplitLast             { typedef NullType Type;
-                                             typedef NullType List; };
-    template
-    struct SplitLast >   { typedef TY       Type;
-                                             typedef NullType List; };
-    
-    template
-    struct SplitLast >      { typedef typename SplitLast::Type Type;
-                                             typedef typename Append< TY,
-                                                                      typename SplitLast::List
-                                                                    >::List 
-                                                                    List; };
-    
-    
-    
-    /** 
-     * splice a typelist like an overlay
-     * into an base typelist, starting at given index.
-     * @return either the combined (spliced) List, or
-     *         the Front/Back part before or after the Overlay
-     * @note using a NullType as OVERLAY allows to extract
-     *         an arbitrary Front/Back part of the list
-     */
-    template
-    struct Splice;
-    
-    template
-    struct Splice, OVERLAY, i>  { typedef Node::List>  List;
-                                             typedef Node::Front> Front;
-                                             typedef         typename Splice::Back   Back; };
-    
-    template
-    struct Splice,Node,0> { typedef Node::List>          List;
-                                             typedef NullType                                          Front;
-                                             typedef         typename Splice::Back           Back; };
-    
-    template
-    struct Splice, NullType, 0> { typedef Node List;
-                                             typedef NullType    Front;
-                                             typedef Node Back; };
-    
-    template
-    struct Splice         { typedef NullType    List;
-                                             typedef NullType    Front;
-                                             typedef NullType    Back; };
-    
-    
-    
-    
     /**
-     * Allows to access various parts of a given typelist:
-     * Start and End, Prefix and Tail..
+     * Metafunction to check if a specific type is contained
+     * in a given typelist. Only exact match is detected.
      */
-    template
-    struct Dissect;
-    
-    template
-    struct Dissect >
+    template
+    struct IsInList
       {
-        typedef Node                  List;  ///< the complete list
-        typedef T                              Head;  ///< first element
-        typedef Node               First; ///< a list containing the first element
-        typedef TYPES                          Tail;  ///< remainder of the list starting with the second elm.
-        typedef typename SplitLast::List Prefix;///< all of the list, up to but excluding the last element
-        typedef typename SplitLast::Type End;   ///< the last element
-        typedef Node             Last;  ///< a list containing the last element
+        enum{ value = false };
       };
     
-    template<>
-    struct Dissect
+    template
+    struct IsInList >
       {
-        typedef NullType                       List;
-        typedef NullType                       Head;
-        typedef NullType                       First;
-        typedef NullType                       Tail;
-        typedef NullType                       Prefix;
-        typedef NullType                       End;
-        typedef NullType                       Last;
+        enum{ value = true };
       };
     
-    
-    
-    
-    /** 
-     * prefix each of the elements,
-     * yielding a list-of lists-of-types
-     */
-    template
-    struct PrefixAll                       { typedef Node< typename Append::List, NullType>  List; };
-    
-    template
-    struct PrefixAll          { typedef NullType  List; };
-    
-    template
-    struct PrefixAll          { typedef Node< typename Append::List, NullType>  List; };
-    
-    template< class T
-            , class TY, class TYPES
-            >
-    struct PrefixAll >   { typedef Node< typename Append::List
-                                                         , typename PrefixAll::List
-                                                         >     List; };
-    
-    
-    
-    
-    /**
-     * build a list-of lists, where each element of the first arg list
-     * gets in turn prepended to all elements of the second arg list.
-     * Can be used to build all possible combinations from two
-     * sources, i.e. the Cartesian product.
-     */
-    template
-    struct Distribute                      { typedef typename PrefixAll::List  List; };
-    
-    template
-    struct Distribute         { typedef NullType List; };
-    
-    template< class TY, class TYPES
-            , class TAIL
-            >
-    struct Distribute,TAIL> { typedef typename Append< typename PrefixAll::List
-                                                                    , typename Distribute::List
-                                                                    >::List                    
-                                                                    List; };
-    
-    
-    
-    /** 
-     * build all possible combinations, based on a enumeration of the basic cases.
-     * For each of the types in the argument list, an "enumeration generator" template is invoked,
-     * yielding a list of the possible base cases. These base cases are then combined with all the
-     * combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered"
-     * means that the base cases of the n-th element will appear in the n-th position of the
-     * resulting lists,
-     * 
-     * For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a
-     * list-of-lists, featuring all possibilities to combine these distinct toggles. 
-     */
-    template< class X
-            , template class _ENUM_>
-    struct Combine                         { typedef typename Distribute< typename _ENUM_::List
-                                                                        , Node
-                                                                        >::List  List; };
-    template< template class _ENUM_>
-    struct Combine      { typedef NodeNull                    List; };
-    
-    template< class TY, class TYPES
-            , template class _ENUM_>
-    struct Combine,_ENUM_>  { typedef typename Distribute< typename _ENUM_::List
-                                                                        , typename Combine::List
-                                                                        >::List  List; };
-    
-    /** enumeration generator for the Combine metafunction,
-     *  yielding an "on" and "off" case
-     */
-    template
-    struct FlagOnOff
-      { 
-        typedef Node >  List;
-      };
-    
-    
-    /** generate all possible on-off combinations of the given flags */
-    template
-    struct CombineFlags
-      { 
-        typedef typename Combine::List  List; 
+    template
+    struct IsInList >
+      {
+        enum{ value = IsInList::value };
       };
     
+    /** convenience shortcut: query function */
+    template
+    bool
+    isInList()
+    {
+      return IsInList::value;
+    }
     
     
     
diff --git a/src/lib/meta/typelist.hpp b/src/lib/meta/typelist.hpp
index 5d0e11bc0..3de24176f 100644
--- a/src/lib/meta/typelist.hpp
+++ b/src/lib/meta/typelist.hpp
@@ -40,15 +40,25 @@ This code is heavily inspired by
 
 /** @file typelist.hpp
  ** A template metaprogramming technique for manipulating collections of types.
- ** Effectively this is a taylored and simplified version of what can be found in the Loki library.
+ ** Effectively this is a tailored and simplified version of what can be found in the Loki library.
  ** We use it in other generic library-style code to generate repetitive code. If you tend to find
  ** template metaprogramming (or functional programming in general) offending, please ignore the 
- ** technical details and just consider the benefit of such an simplification for the user code.
- **
- ** Interface for using this facility is the template Types(.....) for up to 20 Type parameters
+ ** technical details and just consider the benefit of such an simplification for the client code.
  **
+ ** Interface for using this facility is the template Types(.....) for up to 20 Type parameters.
+ ** To start typelist processing, other templates typically pick up the Types<...>::List type.
+ ** This allows for LISP-style list processing, with a pattern match on either Node
+ ** or NullType to terminate recursion. In C++ template metaprogramming, "pattern match"
+ ** is done by partial template specialisations (the compiler will pick up and thus
+ ** match the template parameters). A typedef acts like a declaration in normal
+ ** programming. Because such a "declaration" can't be changed after the fact,
+ ** effectively this is a flavour of functional programming. Just the
+ ** "execution environment" is the compiler, during compilation.
+ ** 
  ** @see lumiera::visitor::Applicable usage example
- ** @see typelisttest.cpp
+ ** @see control::CommandSignature more elaborate usage example (dissecting a functor signature)
+ ** @see TypeList_test
+ ** @see TypeListManip_test
  ** 
  */
 
diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp
index 71b0438fa..0cbf6b5c9 100644
--- a/src/lib/simple-allocator.hpp
+++ b/src/lib/simple-allocator.hpp
@@ -49,6 +49,7 @@
 //#include "pre.hpp"
 #include "lib/error.hpp"
 #include "lib/meta/generator.hpp"
+#include "lib/meta/typelist-util.hpp"
 #include "lib/format.hpp"
 //#include "lib/typed-counter.hpp"
 #include "include/logging.h"
@@ -63,31 +64,11 @@
 namespace lib {
   
 //  using std::tr1::shared_ptr;
-  using lumiera::typelist::InstantiateForEach;
   using lumiera::typelist::Types;
+  using lumiera::typelist::IsInList;
+  using lumiera::typelist::InstantiateForEach;
   
   namespace {
-    using lumiera::typelist::Node;
-    using lumiera::typelist::NullType;
-    
-    template
-    struct IsInList
-      {
-        enum{ value = false };
-      };
-    
-    template
-    struct IsInList, TY>
-      {
-        enum{ value = true };
-      };
-    
-    template
-    struct IsInList, TY>
-      {
-        enum{ value = IsInList::value };
-      };
-    
   }
   
   
@@ -141,9 +122,9 @@ namespace lib {
       ___assertSupportedType()
         {
           typedef typename TYPES::List PreconfiguredTypes;
-          typedef IsInList IsPreconfiguredType;
+          typedef IsInList IsSupportedType;
           
-          BOOST_STATIC_ASSERT (IsPreconfiguredType::value);
+          BOOST_STATIC_ASSERT (IsSupportedType::value);
         }
      
         
diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp
index 7b30ba63c..b4eef9b76 100644
--- a/src/lib/variant.hpp
+++ b/src/lib/variant.hpp
@@ -45,7 +45,7 @@
 #define LIB_VARIANT_H
 
 
-#include "lib/meta/typelist-manip.hpp"
+#include "lib/meta/typelist-util.hpp"
 #include "lib/meta/generator.hpp"
 
 #include 
diff --git a/src/proc/control/command-signature.hpp b/src/proc/control/command-signature.hpp
index 52e4dcf19..44354c300 100644
--- a/src/proc/control/command-signature.hpp
+++ b/src/proc/control/command-signature.hpp
@@ -30,7 +30,7 @@
  ** we need to accept functor objects with a very specific and predetermined
  ** signature, thus allowing for strict type checking by the compiler.
  ** 
- ** \par Relation of function signatures
+ ** \par Relation of function signatures (MEM = type of the "memento" for Undo)
  ** - operation: void(P1,..PN)
  ** - captureUndo: MEM(P1,..PN)
  ** - undoOperation void(P1,..PN,MEM)
@@ -59,24 +59,20 @@
 
 namespace control {
   
-//  using lib::Symbol;
-//  using std::tr1::shared_ptr;
   using std::tr1::function;
   
   using lumiera::typelist::FunctionSignature;
   using lumiera::typelist::FunctionTypedef;
   using lumiera::typelist::Types;
-//using lumiera::typelist::NullType;
-//using lumiera::typelist::Tuple;
   using lumiera::typelist::Append;
   using lumiera::typelist::SplitLast;
   
-
+  
   /** 
-   * Metaprogramming helper for building Command function signatures. 
+   * Metaprogramming helper for building Command function signatures.
    * The complete definition context of any command is templated to the signature
    * of the actual command operation and to the memento type. The typedefs embedded
-   * within CommandSignature allows accepting suitable typed functions
+   * within CommandSignature allows for accepting suitable typed functions
    * to implement the command in question.
    */
   template
@@ -90,7 +86,7 @@ namespace control {
       
     public:
       typedef typename FunctionTypedef::Sig          OperateSig;
-      typedef typename FunctionTypedef::Sig           CaptureSig;
+      typedef typename FunctionTypedef::Sig          CaptureSig;
       typedef typename FunctionTypedef::Sig  UndoOp_Sig;
       typedef Args                                               CmdArgs;
       typedef MEM                                                Memento;
@@ -100,16 +96,23 @@ namespace control {
   
   
   /** 
-   * Type analysis helper template. 
+   * Type analysis helper template.
    * Used for dissecting a given type signature to derive
    * the related basic operation signature, the signature of a possible Undo-function
    * and the signature necessary for capturing undo information. The implementation
    * relies on re-binding an embedded type defining template, based on the actual
    * case, as identified by the structure of the given parameter signature.
+   * 
+   * To use this template, it is instantiated with the signature of a functor object
+   * in question. Depending on the actual situation, the compiler will then either
+   * pick Case1 or Case2 -- thus allowing the client in any case to pick up the
+   * correct signatures for Operation, Capture and Undo-function from the
+   * public typedefs within \c UndoSignature
    */
   template
   class UndoSignature
     {
+      // preparation:  dissect the function signature into arguments and result
       typedef typename FunctionSignature< function >::Args Args;
       typedef typename FunctionSignature< function >::Ret  Ret;
       
diff --git a/tests/40components.tests b/tests/40components.tests
index 9dfaffd74..d0f849145 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -801,6 +801,11 @@ return: 0
 END
 
 
+TEST "simple typelist utils" TypeListUtil_test < >
 out: ctor DoIt >
diff --git a/tests/lib/meta/typelist-util-test.cpp b/tests/lib/meta/typelist-util-test.cpp
new file mode 100644
index 000000000..22a8c62e8
--- /dev/null
+++ b/tests/lib/meta/typelist-util-test.cpp
@@ -0,0 +1,83 @@
+/*
+  TypeListUtil(Test)  -  check the simple typelist metaprogramming helpers 
+
+  Copyright (C)         Lumiera.org
+    2011,               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 "lib/test/run.hpp"
+#include "lib/meta/typelist-util.hpp"
+
+
+
+namespace lumiera {
+namespace typelist{
+namespace test {
+  
+  
+  
+  typedef Types< int
+               , uint
+               , int64_t
+               , uint64_t
+               >::List     TheList;
+  
+  typedef Types<  >::List  EmptyList;
+  
+  
+  
+  /*************************************************************************
+   * @test verify the simple helpers for working with lists-of-types.
+   *       These are simple metafunctions to count the number of elements,
+   *       calculate the maximum size or check for inclusion.
+   *       
+   *       Because these metafunctions will be computed during compilation,
+   *       mostly the function is already verified when it passes compilation.
+   *       All we can do here, at runtime, is to verify some of the (expected)
+   *       constant results.
+   */
+  class TypeListUtil_test : public Test
+    {
+      void
+      run (Arg) 
+        {
+          CHECK (4 == count::value);
+          CHECK (0 == count::value);
+          
+          CHECK (sizeof(int64_t) == maxSize::value);
+          CHECK (0               == maxSize::value);
+          
+          CHECK ( bool(IsInList::value));
+          CHECK ( bool(IsInList::value));
+          CHECK ( bool(IsInList::value));
+          CHECK ( bool(IsInList::value));
+          
+          CHECK (!bool(IsInList::value));
+          CHECK (!bool(IsInList::value));
+          CHECK (!bool(IsInList::value));         // Note: not-a-typelist yields false too
+        } 
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (TypeListUtil_test, "unit common");
+  
+  
+  
+}}} // namespace lumiera::typelist::test

From f505c46d1d83c49fa525d97d414a756a578c0f25 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Sep 2011 19:23:45 +0200
Subject: [PATCH 084/296] finish simple allocator frontend. Unit-test pass

---
 src/lib/simple-allocator.hpp        | 87 ++++++++++++++++++++++++-----
 tests/40components.tests            |  2 +-
 tests/lib/simple-allocator-test.cpp | 16 ++++--
 3 files changed, 86 insertions(+), 19 deletions(-)

diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp
index 0cbf6b5c9..b79b2081d 100644
--- a/src/lib/simple-allocator.hpp
+++ b/src/lib/simple-allocator.hpp
@@ -51,11 +51,9 @@
 #include "lib/meta/generator.hpp"
 #include "lib/meta/typelist-util.hpp"
 #include "lib/format.hpp"
-//#include "lib/typed-counter.hpp"
+#include "lib/typed-counter.hpp"
 #include "include/logging.h"
 
-
-//#include 
 #include 
 #include 
 
@@ -63,24 +61,71 @@
 
 namespace lib {
   
-//  using std::tr1::shared_ptr;
   using lumiera::typelist::Types;
   using lumiera::typelist::IsInList;
   using lumiera::typelist::InstantiateForEach;
   
-  namespace {
-  }
   
   
+  /* === Policies for simple custom allocator  === */
+  
+  /**
+   * Policy: use just plain heap allocations
+   */
   template
   class CustomAllocator
     : public std::allocator
+    { };
+  
+  
+  /**
+   * Policy: maintain explicit per type instance count
+   * @note this imposes additional locking
+   */
+  struct UseInstantiationCounting
     {
+      template
+      size_t
+      allocationCount()  const
+        {
+          return allocCnt_.get();
+        }
       
+      template
+      void
+      incrementCount()
+        {
+          allocCnt_.inc();
+        }
+      
+      template
+      void
+      decrementCount()
+        {
+          allocCnt_.dec();
+        }
+      
+    private:
+      lib::TypedCounter allocCnt_;
     };
   
+  /**
+   * Policy: no additional instantiation accounting
+   */
+  struct NoInstantiationCount
+    {
+      template  size_t allocationCount()  const { return 0; }
+      template  void    incrementCount()  { /* NOP */ }
+      template  void    decrementCount()  { /* NOP */ }
+    };
     
   
+  
+  
+  
+  
+  /* === Allocator frontend === */
+  
   /**
    * Frontend for explicit allocations, using a custom allocator.
    * This template is to be instantiated for the collection of types
@@ -90,13 +135,16 @@ namespace lib {
    * 
    * @todo currently (as of 8/09) the low-level pooled allocator
    *       isn't implemented; instead we do just heap allocations.
-   *       see Ticket #835
+   *       ////////////////////////////////////////////////////////////////////////////////////////////Ticket #835
    */
-  template
+  template
   class SimpleAllocator
-    : InstantiateForEach< typename TYPES::List     // for each of those types...         
-                        , CustomAllocator         //  ...mix in the custom allocator 
+    : InstantiateForEach< typename TYPES::List     // for each of those types...
+                        , CustomAllocator         //  ...mix in the custom allocator
                         >
+    , COUNTER
     {
       
       /** forward plain memory allocation */
@@ -105,7 +153,9 @@ namespace lib {
       allocateSlot ()
         {
           TRACE (memory, "allocate %s", util::tyStr().c_str());
-          return CustomAllocator::allocate (1);
+          XX * newStorage = CustomAllocator::allocate (1);
+          COUNTER::template incrementCount();
+          return newStorage;
         }
       
       template
@@ -114,6 +164,7 @@ namespace lib {
         {
           TRACE (memory, "release %s", util::tyStr().c_str());
           CustomAllocator::deallocate (entry, 1);
+          COUNTER::template decrementCount();
         }
       
       
@@ -126,8 +177,8 @@ namespace lib {
           
           BOOST_STATIC_ASSERT (IsSupportedType::value);
         }
-     
-        
+      
+      
       
       
       public: /* ==== build objects with managed allocation ==== */
@@ -234,10 +285,18 @@ namespace lib {
           releaseSlot (entry);
         }
       
+      
+      /** diagnostics */
+      template
+      size_t
+      numSlots()  const
+        {
+          return COUNTER::template allocationCount();
+        }
     };
   
   
   
-
+  
 } // namespace lib
 #endif
diff --git a/tests/40components.tests b/tests/40components.tests
index d0f849145..615fe5bfe 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -1141,7 +1141,7 @@ return: 0
 END
 
 
-PLANNED "custom allocator(I)" SimpleAllocator_test <
 #include 
 #include 
 
@@ -36,7 +34,6 @@ namespace test{
   
   
   using util::isSameObject;
-//  using std::tr1::shared_ptr;
   using std::string;
   using std::rand;
   
@@ -62,6 +59,13 @@ namespace test{
               checksum_ += (crap_[i] = rand() % 128);
           }
         
+        DummyObj (DummyObj const& o)
+          {
+            REQUIRE (siz);
+            for (uint i=0; i,DummyObj<23>,string> > TestAllocator;
+    typedef Types,DummyObj<23>,string> SupportedTypes;
+    typedef SimpleAllocator TestAllocator;
   }
   
   
@@ -148,6 +153,9 @@ namespace test{
           allocator.destroy (pDxx);
           allocator.destroy (pSxx);
           
+          CHECK (0 == allocator.numSlots >());
+          CHECK (0 == allocator.numSlots >());
+          CHECK (0 == allocator.numSlots());
           CHECK (0 == checksum_);
         }
     };

From 7459c0a41fe947789a1b558ebdf30245af9ef771 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 26 Sep 2011 02:19:31 +0200
Subject: [PATCH 085/296] (cont) buffer metadate implementation

state transitions etc
---
 src/proc/engine/buffer-metadata.hpp | 119 +++++++++++++++++++++++++---
 1 file changed, 106 insertions(+), 13 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index ae59f93f6..cf3edb02e 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -64,6 +64,9 @@ namespace engine {
   
   using lib::Literal;
   
+  namespace error = lumiera::error;
+//using error::LUMIERA_ERROR_INVALID;
+
   
   typedef uint64_t LocalKey;
   typedef size_t HashVal;
@@ -93,7 +96,7 @@ namespace engine {
       
       template
       TypeHandler()
-        : createAttached (0)    /////////TODO: how to attach the ctor function??? mabye better use a class with virtual functions?
+        : createAttached (0)    /////////TODO: how to attach the ctor function??? Maybe better use a class with virtual functions?
         , destroyAttached (0)
         { }
     };
@@ -106,13 +109,18 @@ namespace engine {
     public:
       class Entry
         {
+          HashVal parent_;
+          
+        protected:
+          Entry (HashVal parent) : parent_(parent) { }
+         ~Entry ()                                 { }
           
-        
         public:
-          BufferState state()  const;
-          HashVal parentKey()  const;
-          Entry& mark (BufferState newState);
-          Entry& markLocked (const void* buffer);
+          HashVal parentKey()  const { return parent_;}
+          
+          virtual BufferState state()  const              =0;
+          virtual Entry& mark (BufferState newState)      =0;
+          virtual Entry& markLocked (const void* buffer)  =0;
         };
       
       
@@ -172,6 +180,98 @@ namespace engine {
     
   /* === Implementation === */
   
+  namespace {
+
+    using error::LUMIERA_ERROR_LIFECYCLE;
+    using error::LUMIERA_ERROR_BOTTOM_VALUE;
+    
+    
+    class TypeEntry
+      : public Metadata::Entry
+      {
+        virtual BufferState
+        state()  const
+          {
+            return NIL;
+          }
+        
+        virtual Entry&
+        mark (BufferState newState)
+          {
+            if (newState != NIL)
+              throw error::Logic ("This metadata entry is still abstract. "
+                                  "The only possible state transition is to markLocked (buffer)."
+                                 , LUMIERA_ERROR_LIFECYCLE
+                                 );
+            return *this;
+          }
+        
+        virtual Entry&
+        markLocked (const void* buffer)
+          {
+            UNIMPLEMENTED ("how to invoke the allocator. We need to allocate a BufferEntry here");
+          }
+      };
+    
+    
+    class BufferEntry
+      : public Metadata::Entry
+      {
+        BufferState state_;
+        const void* buffer_;
+        
+        virtual BufferState
+        state()  const
+          {
+            return state_;
+          }
+        
+        virtual Entry&
+        markLocked (const void* buffer)
+          {
+            if (!buffer)
+              throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
+                                 , LUMIERA_ERROR_BOTTOM_VALUE);
+            if (this->buffer_)
+              throw error::Logic ("Attempt to lock a slot for a new buffer, "
+                                  "while actually the old buffer is still locked."
+                                 , LUMIERA_ERROR_LIFECYCLE );
+            this->buffer_ = buffer;
+            state_ = LOCKED;
+          }
+        
+        virtual Entry&
+        mark (BufferState newState)
+          {
+            switch (this->state_)
+              {
+              case NIL:
+                throw error::Fatal ("Concrete buffer entry with state==NIL encountered."
+                                    "State transition logic broken (programming error)");
+              case FREE:
+                throw error::Logic ("Need a new buffer pointer in order to lock an entry."
+                                   , LUMIERA_ERROR_LIFECYCLE );
+              case LOCKED:
+                if (newState == EMITTED) break; // allow transition
+                
+              case EMITTED:
+                if (newState == BLOCKED) break; // allow transition
+                
+              case BLOCKED:
+                if (newState == FREE)           // note fall through for LOCKED and EMITTED too 
+                  {
+                    buffer_ = 0;
+                    break; // allow transition
+                  }
+              default:
+                throw error::Fatal ("Invalid buffer state encountered.");
+              }
+            state_ = newState;
+            return *this;
+          }
+      };
+  }
+  
   /** */
   BufferState
   Metadata::Entry::state ()  const
@@ -179,13 +279,6 @@ namespace engine {
       UNIMPLEMENTED ("buffer state accounting");
     }
   
-  /** */
-  HashVal
-  Metadata::Entry::parentKey ()  const
-    {
-      UNIMPLEMENTED ("retrieve the sparent (object or type) key");
-    }
-  
   Metadata::Entry&
   Metadata::Entry::mark (BufferState newState)
     {

From 4df45c44e9fb4771df5e40984b5383246bb8daa1 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 6 Oct 2011 21:55:00 +0200
Subject: [PATCH 086/296] BufferMetadata: some musing about how to access the
 Allocator

---
 src/proc/engine/buffer-metadata.hpp           | 32 +++++++++----------
 .../proc/engine/buffer-metadata-test.cpp      |  2 +-
 2 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index cf3edb02e..42c94c208 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -120,7 +120,6 @@ namespace engine {
           
           virtual BufferState state()  const              =0;
           virtual Entry& mark (BufferState newState)      =0;
-          virtual Entry& markLocked (const void* buffer)  =0;
         };
       
       
@@ -159,6 +158,9 @@ namespace engine {
           UNIMPLEMENTED ("access, possibly create metadata records");
         }
       
+      Entry&
+      markLocked (HashVal parentKey, const void* buffer);
+      
       bool
       isKnown (HashVal key)  const
         {
@@ -206,11 +208,6 @@ namespace engine {
             return *this;
           }
         
-        virtual Entry&
-        markLocked (const void* buffer)
-          {
-            UNIMPLEMENTED ("how to invoke the allocator. We need to allocate a BufferEntry here");
-          }
       };
     
     
@@ -229,15 +226,6 @@ namespace engine {
         virtual Entry&
         markLocked (const void* buffer)
           {
-            if (!buffer)
-              throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
-                                 , LUMIERA_ERROR_BOTTOM_VALUE);
-            if (this->buffer_)
-              throw error::Logic ("Attempt to lock a slot for a new buffer, "
-                                  "while actually the old buffer is still locked."
-                                 , LUMIERA_ERROR_LIFECYCLE );
-            this->buffer_ = buffer;
-            state_ = LOCKED;
           }
         
         virtual Entry&
@@ -287,10 +275,20 @@ namespace engine {
     }
   
   Metadata::Entry&
-  Metadata::Entry::markLocked (const void* buffer)
+  Metadata::markLocked (HashVal parentKey, const void* buffer)
     {
       UNIMPLEMENTED ("transition to locked state");
-      return *this;
+      if (!buffer)
+        throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
+                           , LUMIERA_ERROR_BOTTOM_VALUE);
+      
+      HashVal newKey = this->key (parentKey, buffer);
+      if (isLocked(newKey))
+        throw error::Logic ("Attempt to lock a slot for a new buffer, "
+                            "while actually the old buffer is still locked."
+                           , LUMIERA_ERROR_LIFECYCLE );
+      
+      return this->get(newKey);
     }
   
   
diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp
index 132500fb1..a03984e11 100644
--- a/tests/components/proc/engine/buffer-metadata-test.cpp
+++ b/tests/components/proc/engine/buffer-metadata-test.cpp
@@ -137,7 +137,7 @@ namespace test  {
           VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
           VERIFY_ERROR (LIFECYCLE, m1.mark(LOCKED)  );
           
-          Metadata::Entry& m2 = m1.markLocked (SOME_POINTER);
+          Metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER);
           CHECK (!isSameObject (m1,m2));
           CHECK (NIL    == m1.state());
           CHECK (LOCKED == m2.state());

From c79b28fe7cb678b99da0b3ce81d2ca118054687e Mon Sep 17 00:00:00 2001
From: "Michael R. Fisher" 
Date: Thu, 6 Oct 2011 11:58:31 -0500
Subject: [PATCH 087/296] Replacing zoomIn and zoomOut buttons in the
 TimelinePanel with a new TimelineZoomScale widget

---
 .../widgets/timeline/timeline-zoom-scale.cpp  | 115 ++++++++++++++++++
 .../widgets/timeline/timeline-zoom-scale.hpp  |  99 +++++++++++++++
 2 files changed, 214 insertions(+)
 create mode 100644 src/gui/widgets/timeline/timeline-zoom-scale.cpp
 create mode 100644 src/gui/widgets/timeline/timeline-zoom-scale.hpp

diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
new file mode 100644
index 000000000..c019d83ed
--- /dev/null
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -0,0 +1,115 @@
+/*
+  timeline-zoom-scale.cpp  -  Implementation of the zoom scale widget
+
+  Copyright (C)         Lumiera.org
+    2011,               Michael R. Fisher 
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of
+  the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+* *****************************************************/
+
+#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
+#include "gui/widgets/timeline-widget.hpp"
+
+using namespace Gtk;
+
+namespace gui {
+namespace widgets {
+
+class TimelineWidget;
+
+namespace timeline {
+
+TimelineZoomScale::TimelineZoomScale()
+  : HBox()
+  , adjustment(0.5, 0.0, 1.0, 0.000001)
+  , slider()
+  , zoomIn(Stock::ZOOM_IN)
+  , zoomOut(Stock::ZOOM_OUT)
+  , smoothing_factor(9.0)
+  , button_step_size(0.03)
+{
+  /* Setup the Slider Control */
+  slider.set_adjustment(adjustment);
+  slider.set_size_request(123,10);
+  slider.set_digits(6);
+  slider.set_inverted(true);
+  slider.set_draw_value(false);
+
+  /* Make our connections */
+  zoomIn.signal_clicked().
+      connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_in_clicked));
+
+  zoomOut.signal_clicked().
+      connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked));
+
+  adjustment.signal_value_changed().
+      connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom));
+
+  /* Add Our Widgets and show them */
+  pack_start(zoomOut,PACK_SHRINK);
+  pack_start(slider,PACK_SHRINK);
+  pack_start(zoomIn,PACK_SHRINK);
+
+  show_all();
+}
+
+void
+TimelineZoomScale::on_zoom_in_clicked()
+{
+  double newValue = adjustment.get_value() - button_step_size;
+  adjustment.set_value(newValue);
+}
+
+void
+TimelineZoomScale::on_zoom_out_clicked()
+{
+  double newValue = adjustment.get_value() + button_step_size;
+  adjustment.set_value(newValue);
+}
+
+void
+TimelineZoomScale::on_zoom()
+{
+  zoomSignal.emit(calculate_zoom_scale()) ;
+}
+
+sigc::signal
+TimelineZoomScale::signal_zoom()
+{
+  return zoomSignal;
+}
+
+int64_t
+TimelineZoomScale::calculate_zoom_scale()
+{
+  int64_t zoom_scale = 0;
+
+  double smoothed = pow(adjustment.get_value(), smoothing_factor);
+  zoom_scale = (int64_t)( smoothed * (double)TimelineWidget::MaxScale);
+
+  /* Prevent Zooming in To Close and Far */
+  if(zoom_scale < 1)
+    zoom_scale = 1;
+
+  if(zoom_scale > TimelineWidget::MaxScale)
+    zoom_scale = TimelineWidget::MaxScale;
+
+  return zoom_scale;
+}
+
+} // namespace gui
+} // namespace widgets
+} // namespace timeline
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
new file mode 100644
index 000000000..ebeaaffca
--- /dev/null
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
@@ -0,0 +1,99 @@
+/*
+  timeline-zoom-scale.hpp  -  Declaration of the zoom scale widget
+
+  Copyright (C)         Lumiera.org
+    2011,               Michael R. Fisher 
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of
+  the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/** @file timeline-zoom-scale.hpp
+ ** This file contains the definition of the zoom scale widget
+ */
+
+#ifndef TIMELINE_ZOOM_SCALE_HPP
+#define TIMELINE_ZOOM_SCALE_HPP
+
+#include "gui/gtk-lumiera.hpp"
+#include "gui/widgets/mini-button.hpp"
+
+using namespace Gtk;
+using namespace gui::widgets;
+
+namespace gui {
+namespace widgets {
+namespace timeline {
+
+class TimelineZoomScale : public Gtk::HBox
+{
+public:
+  /**
+   * Constructor
+   */
+  TimelineZoomScale();
+
+  /**
+   * Accessor method to the zoomSignal
+   * @return the zoomSignal
+   */
+  sigc::signal signal_zoom();
+
+private:
+  /* Event Handlers */
+
+  /**
+   * Event handler for when the zoomIn Button
+   * is clicked
+   */
+  void on_zoom_in_clicked();
+
+  /**
+   * Event handler for when the zoomIn Button
+   * is clicked
+   */
+  void on_zoom_out_clicked();
+
+  /**
+   * Event handler for when the adjustment
+   * value is changed
+   */
+  void on_zoom();
+
+  /**
+   * Calculate a Zoom Scale value based on
+   * the adjustment's current value
+   * @return The Zoom Scale value
+   */
+  int64_t calculate_zoom_scale();
+
+  /* Widgets */
+  Gtk::Adjustment adjustment;
+  Gtk::HScale slider;
+  MiniButton zoomIn;
+  MiniButton zoomOut;
+
+protected:
+  /* Signals */
+  sigc::signal zoomSignal;
+
+  const double smoothing_factor;
+  const double button_step_size;
+};
+
+} // namespace gui
+} // namespace widgets
+} // namespace timeline
+
+#endif /* TIMELINE_ZOOM_SCALE_HPP */

From e0463da20450ec97e4ee65bca64a09f0783faa33 Mon Sep 17 00:00:00 2001
From: "Michael R. Fisher" 
Date: Thu, 6 Oct 2011 12:38:10 -0500
Subject: [PATCH 088/296] Added TimelineZoomScale widget

---
 src/gui/panels/timeline-panel.cpp             | 16 +++++++++++---
 src/gui/panels/timeline-panel.hpp             |  4 ++++
 src/gui/widgets/timeline-widget.cpp           | 10 ++++-----
 src/gui/widgets/timeline-widget.hpp           |  2 +-
 .../widgets/timeline/timeline-view-window.cpp | 22 +++++++++----------
 .../widgets/timeline/timeline-view-window.hpp |  2 +-
 .../widgets/timeline/timeline-zoom-scale.cpp  |  9 ++++++++
 7 files changed, 43 insertions(+), 22 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index 07dce7b1d..a9c967403 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -60,6 +60,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   , iBeamTool(Gtk::StockID("tool_i_beam"))
   , zoomIn(Stock::ZOOM_IN)
   , zoomOut(Stock::ZOOM_OUT)
+  , zoomScale()
   , updatingToolbar(false)
   , currentTool(timeline::Arrow)
 {
@@ -101,9 +102,10 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
     
   toolbar.append(separator2);
   
-  toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in));
-  toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out));
-   
+  toolbar.append(zoomScale);
+  zoomScale.signal_zoom().
+    connect(mem_fun(this,&TimelinePanel::on_zoom));
+
   toolbar.show_all();
   panelBar.pack_start(toolbar, PACK_SHRINK);
 
@@ -183,6 +185,14 @@ TimelinePanel::on_ibeam_tool()
   set_tool(timeline::IBeam);
 }
 
+void
+TimelinePanel::on_zoom(int64_t zoom_scale)
+{
+  REQUIRE(timelineWidget);
+  timelineWidget->zoom_view(zoom_scale);
+  update_zoom_buttons();
+}
+
 void
 TimelinePanel::on_zoom_in()
 {
diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp
index b65469bc9..aac000277 100644
--- a/src/gui/panels/timeline-panel.hpp
+++ b/src/gui/panels/timeline-panel.hpp
@@ -32,6 +32,8 @@
 #include "gui/panels/panel.hpp"
 #include "gui/widgets/timecode-widget.hpp"
 #include "gui/widgets/timeline-widget.hpp"
+#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
+
 #include "lib/time/timevalue.hpp"
 
 #include 
@@ -87,6 +89,7 @@ private:
   void on_arrow_tool();
   void on_ibeam_tool();
   
+  void on_zoom(int64_t zoom_size);
   void on_zoom_in();
   void on_zoom_out();
   
@@ -191,6 +194,7 @@ private:
   
   MiniButton zoomIn;
   MiniButton zoomOut;
+  gui::widgets::timeline::TimelineZoomScale zoomScale;
   
   Gtk::SeparatorToolItem separator2;
     
diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp
index a00882b7e..cd1459cd8 100644
--- a/src/gui/widgets/timeline-widget.cpp
+++ b/src/gui/widgets/timeline-widget.cpp
@@ -129,13 +129,13 @@ TimelineWidget::set_state(shared_ptr new_state)
 }
 
 void
-TimelineWidget::zoom_view(int zoom_size)
+TimelineWidget::zoom_view(int64_t zoom_scale)
 {
   if(state)
-    {
-      const int view_width = body->get_allocation().get_width();
-      state->get_view_window().zoom_view(view_width / 2, zoom_size);
-    }
+  {
+    const int view_width = body->get_allocation().get_width();
+    state->get_view_window().zoom_view(view_width / 2, zoom_scale);
+  }
 }
 
 ToolType
diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp
index ca213c15b..b8371534b 100644
--- a/src/gui/widgets/timeline-widget.hpp
+++ b/src/gui/widgets/timeline-widget.hpp
@@ -98,7 +98,7 @@ public:
    * @param zoom_size The number of steps to zoom by. The scale factor
    * is 1.25^(-zoom_size).
    */
-  void zoom_view(int zoom_size);
+  void zoom_view(int64_t zoom_scale);
   
   /**
    * Gets the type of the tool currently active.
diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp
index d9b86597b..004022235 100644
--- a/src/gui/widgets/timeline/timeline-view-window.cpp
+++ b/src/gui/widgets/timeline/timeline-view-window.cpp
@@ -31,8 +31,6 @@ namespace gui {
 namespace widgets {
 namespace timeline {
 
-
-
 TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale)
   : timeOffset(offset)
   , timeScale(scale)
@@ -66,21 +64,21 @@ TimelineViewWindow::set_time_scale(int64_t scale)
 }
 
 void
-TimelineViewWindow::zoom_view(int point, int zoom_size)
-{ 
-  int64_t new_time_scale = (double)timeScale * pow(1.25, -zoom_size);
-  
+TimelineViewWindow::zoom_view(int point, int64_t zoom_scale)
+{
+  int64_t new_time_scale = zoom_scale;
+
   // Limit zooming in too close
   if(new_time_scale < 1) new_time_scale = 1;
-  
-  // Nudge zoom problems caused by integer rounding
+
+ /* Not sure if this is still needed. MRF
+  *  // Nudge zoom problems caused by integer rounding
   if(new_time_scale == timeScale && zoom_size < 0)
     new_time_scale++;
-    
-  // Limit zooming out too far
-  if(new_time_scale > TimelineWidget::MaxScale)
-    new_time_scale = TimelineWidget::MaxScale;
+  */
   
+  // Limit to Min and Max scale code moved to timeline-zoom-scale.cpp
+
   // The view must be shifted so that the zoom is centred on the cursor
   TimeVar newStartPoint = get_time_offset();
   newStartPoint += TimeValue(point * (timeScale - new_time_scale));
diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp
index 4f1733af8..3bf67ac89 100644
--- a/src/gui/widgets/timeline/timeline-view-window.hpp
+++ b/src/gui/widgets/timeline/timeline-view-window.hpp
@@ -101,7 +101,7 @@ public:
    * @param zoom_size The number of steps to zoom by. The scale factor
    * is 1.25^(-zoom_size).
    */
-  void zoom_view(int point, int zoom_size);
+  void zoom_view(int point, int64_t zoom_size);
   
   /**
    * Scrolls the view horizontally as a proportion of the view area.
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index c019d83ed..901df2d7f 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -32,6 +32,15 @@ class TimelineWidget;
 
 namespace timeline {
 
+/**
+ * TODO: The initial adjustment value needs to
+ * match what the TimelineViewWindow's actual timeScale
+ * Value is. TimelineViewWindow::get_time_scale() is
+ * currently a public method, but will soon be private.
+ * Maybe TimelineViewWindow can have a zoom_adjustment
+ * that gets passed to this widget's Constructor?
+ */
+
 TimelineZoomScale::TimelineZoomScale()
   : HBox()
   , adjustment(0.5, 0.0, 1.0, 0.000001)

From caace00dd5cd18d764d4575ce0316cbcda8ed0dc Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 7 Oct 2011 03:49:51 +0200
Subject: [PATCH 089/296] Review the new TimelineZoomScale widget code

---
 src/gui/panels/timeline-panel.cpp              |  1 +
 src/gui/widgets/timeline/timeline-body.cpp     |  2 +-
 .../widgets/timeline/timeline-view-window.cpp  | 18 ++++--------------
 .../widgets/timeline/timeline-view-window.hpp  |  6 +++---
 .../widgets/timeline/timeline-zoom-scale.cpp   | 17 ++++++++++++++++-
 5 files changed, 25 insertions(+), 19 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index a9c967403..39c4f5b26 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -124,6 +124,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
 
   zoomIn          .set_tooltip_text(_("Zoom in"));
   zoomOut         .set_tooltip_text(_("Zoom out"));
+  zoomScale       .set_tooltip_text(_("Adjust timeline zoom scale"));
 
   // Setup the timeline widget
   shared_ptr sequence          ///////////////////////////////TICKET #796
diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp
index 7f78d0929..b0818653a 100644
--- a/src/gui/widgets/timeline/timeline-body.cpp
+++ b/src/gui/widgets/timeline/timeline-body.cpp
@@ -264,7 +264,7 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
           {
             TimelineViewWindow &window = view_window();
             
-                                   /////////////////////////////TICKET# 795 : don't reach in from outside and manipulate internals of the timeline view!
+                                   /////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view!
                                    /////////////////////////////            : either encapsulate this entirely here, or leave it to the timeline view!            
             const int64_t scale = window.get_time_scale();
             window.set_time_offset(beginShiftTimeOffset
diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp
index 004022235..84379b17c 100644
--- a/src/gui/widgets/timeline/timeline-view-window.cpp
+++ b/src/gui/widgets/timeline/timeline-view-window.cpp
@@ -38,20 +38,20 @@ TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale)
 }
 
 Offset
-TimelineViewWindow::get_time_offset() const      /////////////////////TODO: this function shouldn't be accessible from outside
+TimelineViewWindow::get_time_offset() const      /////////////////////TICKET #795: this function shouldn't be accessible from outside
 {
   return Offset (timeOffset);
 }
 
 void
-TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TODO: this function shouldn't be accessible from outside
+TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TICKET #795: this function shouldn't be accessible from outside
 {
   timeOffset = offset;
   changedSignal.emit();
 }
 
 int64_t
-TimelineViewWindow::get_time_scale() const       /////////////////////TODO: this function shouldn't be accessible from outside
+TimelineViewWindow::get_time_scale() const       /////////////////////TICKET #795: this function shouldn't be accessible from outside
 {
   return timeScale;
 }
@@ -64,21 +64,11 @@ TimelineViewWindow::set_time_scale(int64_t scale)
 }
 
 void
-TimelineViewWindow::zoom_view(int point, int64_t zoom_scale)
+TimelineViewWindow::zoom_view(int point, int64_t new_time_scale)
 {
-  int64_t new_time_scale = zoom_scale;
-
   // Limit zooming in too close
   if(new_time_scale < 1) new_time_scale = 1;
 
- /* Not sure if this is still needed. MRF
-  *  // Nudge zoom problems caused by integer rounding
-  if(new_time_scale == timeScale && zoom_size < 0)
-    new_time_scale++;
-  */
-  
-  // Limit to Min and Max scale code moved to timeline-zoom-scale.cpp
-
   // The view must be shifted so that the zoom is centred on the cursor
   TimeVar newStartPoint = get_time_offset();
   newStartPoint += TimeValue(point * (timeScale - new_time_scale));
diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp
index 3bf67ac89..496085555 100644
--- a/src/gui/widgets/timeline/timeline-view-window.hpp
+++ b/src/gui/widgets/timeline/timeline-view-window.hpp
@@ -98,10 +98,10 @@ public:
   /**
    * Zooms the view in or out as by a number of steps while keeping a 
    * given point on the timeline still.
-   * @param zoom_size The number of steps to zoom by. The scale factor
-   * is 1.25^(-zoom_size).
+   * @param new_time_scale The number of steps to zoom by. The scale factor
+   * is 1.25^(-new_time_scale).
    */
-  void zoom_view(int point, int64_t zoom_size);
+  void zoom_view(int point, int64_t new_time_scale);
   
   /**
    * Scrolls the view horizontally as a proportion of the view area.
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index 901df2d7f..9129a5ce5 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -33,12 +33,27 @@ class TimelineWidget;
 namespace timeline {
 
 /**
- * TODO: The initial adjustment value needs to
+ * @todo The initial adjustment value needs to
  * match what the TimelineViewWindow's actual timeScale
  * Value is. TimelineViewWindow::get_time_scale() is
  * currently a public method, but will soon be private.
  * Maybe TimelineViewWindow can have a zoom_adjustment
  * that gets passed to this widget's Constructor?
+ * 
+ * @todo actually there is a more involved problem.
+ * The TimelineWidget maintains a TimelineState, which in turn
+ * owns the TimelineViewWindow. Now, the problem is: when we
+ * switch to another Sequence (View), then this TimelineState
+ * gets switched too, causing also a entirely different TimelineViewWindow
+ * to become effective. Thus
+ * - how can we managed to be notified from that switch?
+ * - problem is: TimelineZoomScale widget is owned by the TimelinePannel.
+ *   Likewise, TimelineWidget is owned by the TimelinePannel. But the
+ *   state handling/switching logic is embedded within TimelineWidget
+ * - and finally: how can we translate the actual scale (in time units),
+ *   as maintained within TimelineViewWindow, back into the adjustment
+ *   used here (which uses a relative scale 0...1.0 ) 
+ * 
  */
 
 TimelineZoomScale::TimelineZoomScale()

From 3acf8049888846956cbd8ccd95c78bad46e8a1b2 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 8 Oct 2011 02:21:29 +0200
Subject: [PATCH 090/296] flesh out the buffer metadata API

functionality just stubbed still
---
 src/proc/engine/buffer-metadata.hpp           | 120 +++++++++++-------
 .../proc/engine/buffer-metadata-test.cpp      |  22 +++-
 2 files changed, 95 insertions(+), 47 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index 42c94c208..e3a071259 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -57,7 +57,7 @@
 #include "lib/error.hpp"
 #include "lib/symbol.hpp"
 
-//#include 
+#include 
 
 
 namespace engine {
@@ -65,8 +65,7 @@ namespace engine {
   using lib::Literal;
   
   namespace error = lumiera::error;
-//using error::LUMIERA_ERROR_INVALID;
-
+  
   
   typedef uint64_t LocalKey;
   typedef size_t HashVal;
@@ -105,6 +104,7 @@ namespace engine {
   
   
   class Metadata
+    : boost::noncopyable
     {
     public:
       class Entry
@@ -119,6 +119,7 @@ namespace engine {
           HashVal parentKey()  const { return parent_;}
           
           virtual BufferState state()  const              =0;
+          virtual const void* access() const              =0;
           virtual Entry& mark (BufferState newState)      =0;
         };
       
@@ -158,9 +159,6 @@ namespace engine {
           UNIMPLEMENTED ("access, possibly create metadata records");
         }
       
-      Entry&
-      markLocked (HashVal parentKey, const void* buffer);
-      
       bool
       isKnown (HashVal key)  const
         {
@@ -173,8 +171,12 @@ namespace engine {
           UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
         }
       
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
+      
+      /* == memory management == */
+      
+      Entry& markLocked (HashVal parentKey, const void* buffer);
+      void release (HashVal key);
+      
     };
     
     
@@ -183,7 +185,7 @@ namespace engine {
   /* === Implementation === */
   
   namespace {
-
+    
     using error::LUMIERA_ERROR_LIFECYCLE;
     using error::LUMIERA_ERROR_BOTTOM_VALUE;
     
@@ -197,6 +199,16 @@ namespace engine {
             return NIL;
           }
         
+        virtual const void*
+        access()  const
+          {
+            throw error::Logic ("This metadata entry is still abstract. "
+                                "It can't be associated with a concrete buffer. "
+                                "You need to invoke markLocked (buffer)."
+                               , LUMIERA_ERROR_LIFECYCLE
+                               );
+          }
+        
         virtual Entry&
         mark (BufferState newState)
           {
@@ -220,12 +232,18 @@ namespace engine {
         virtual BufferState
         state()  const
           {
+            __must_not_be_NIL();
             return state_;
           }
         
-        virtual Entry&
-        markLocked (const void* buffer)
+        virtual const void*
+        access()  const
           {
+            __must_not_be_NIL();
+            __must_not_be_FREE();
+            
+            ENSURE (buffer_);
+            return buffer_;
           }
         
         virtual Entry&
@@ -233,12 +251,9 @@ namespace engine {
           {
             switch (this->state_)
               {
-              case NIL:
-                throw error::Fatal ("Concrete buffer entry with state==NIL encountered."
-                                    "State transition logic broken (programming error)");
-              case FREE:
-                throw error::Logic ("Need a new buffer pointer in order to lock an entry."
-                                   , LUMIERA_ERROR_LIFECYCLE );
+              case NIL:  __must_not_be_NIL();
+              case FREE: __must_not_be_FREE();
+                
               case LOCKED:
                 if (newState == EMITTED) break; // allow transition
                 
@@ -246,7 +261,7 @@ namespace engine {
                 if (newState == BLOCKED) break; // allow transition
                 
               case BLOCKED:
-                if (newState == FREE)           // note fall through for LOCKED and EMITTED too 
+                if (newState == FREE)           // note fall through for LOCKED and EMITTED too
                   {
                     buffer_ = 0;
                     break; // allow transition
@@ -257,39 +272,54 @@ namespace engine {
             state_ = newState;
             return *this;
           }
+        
+        
+        void
+        __must_not_be_NIL()  const
+          {
+            if (NIL == state_)
+              throw error::Fatal ("Concrete buffer entry with state==NIL encountered."
+                                  "State transition logic broken (programming error)");
+          }
+        
+        void
+        __must_not_be_FREE()  const
+          {
+            if (FREE == state_)
+                throw error::Logic ("Buffer is inaccessible (marked as free). "
+                                    "Need a new buffer pointer in order to lock an entry. "
+                                    "You should invoke markLocked(buffer) prior to access."
+                                   , LUMIERA_ERROR_LIFECYCLE );
+          }
       };
   }
   
+  
+  
+  
   /** */
-  BufferState
-  Metadata::Entry::state ()  const
-    {
-      UNIMPLEMENTED ("buffer state accounting");
-    }
-  
-  Metadata::Entry&
-  Metadata::Entry::mark (BufferState newState)
-    {
-      UNIMPLEMENTED ("buffer state transitions");
-      return *this;
-    }
-  
   Metadata::Entry&
   Metadata::markLocked (HashVal parentKey, const void* buffer)
-    {
-      UNIMPLEMENTED ("transition to locked state");
-      if (!buffer)
-        throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
-                           , LUMIERA_ERROR_BOTTOM_VALUE);
-      
-      HashVal newKey = this->key (parentKey, buffer);
-      if (isLocked(newKey))
-        throw error::Logic ("Attempt to lock a slot for a new buffer, "
-                            "while actually the old buffer is still locked."
-                           , LUMIERA_ERROR_LIFECYCLE );
-      
-      return this->get(newKey);
-    }
+  {
+    UNIMPLEMENTED ("transition to locked state");
+    if (!buffer)
+      throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
+                         , LUMIERA_ERROR_BOTTOM_VALUE);
+    
+    HashVal newKey = this->key (parentKey, buffer);
+    if (isLocked(newKey))
+      throw error::Logic ("Attempt to lock a slot for a new buffer, "
+                          "while actually the old buffer is still locked."
+                         , LUMIERA_ERROR_LIFECYCLE );
+    
+    return this->get(newKey);
+  }
+  
+  void
+  Metadata::release (HashVal key)
+  {
+    UNIMPLEMENTED ("metadata memory management");
+  }
   
   
   
diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp
index a03984e11..fd55bf4c8 100644
--- a/tests/components/proc/engine/buffer-metadata-test.cpp
+++ b/tests/components/proc/engine/buffer-metadata-test.cpp
@@ -141,6 +141,7 @@ namespace test  {
           CHECK (!isSameObject (m1,m2));
           CHECK (NIL    == m1.state());
           CHECK (LOCKED == m2.state());
+          CHECK (SOME_POINTER == m2.access());
           
           HashVal keyX = meta_->key(key1, SOME_POINTER);
           CHECK (meta_->isLocked(keyX));
@@ -152,11 +153,28 @@ namespace test  {
           CHECK ( isSameObject (m2, meta_->get(keyX)));
           CHECK ( key1 == m2.parentKey());
           
-          m2.mark(FREE);              // Warning: don't use the m2 reference anymore! 
+          // now able to do state transitions
+          CHECK (LOCKED == m2.state());
+          m2.mark(EMITTED);
+          CHECK (EMITTED == m2.state());
+          CHECK (SOME_POINTER == m2.access());
+          CHECK ( meta_->isLocked(keyX));
+          CHECK ( meta_->isKnown(keyX));
+          
+          // but the FREE state is a dead end
+          m2.mark(FREE); 
+          CHECK (!meta_->isLocked(keyX));
+          CHECK ( meta_->isKnown(keyX));
+          CHECK ( meta_->isKnown(key1));
+          VERIFY_ERROR (LIFECYCLE, m2.access());
+          VERIFY_ERROR (LIFECYCLE, m2.mark(LOCKED));
+          CHECK ( isSameObject (m2, meta_->get(keyX))); // still accessible
+          
+          meta_->release(keyX);
           CHECK (!meta_->isLocked(keyX));
           CHECK (!meta_->isKnown(keyX));
           CHECK ( meta_->isKnown(key1));
-          VERIFY_ERROR (INVALID, meta_->get(keyX));
+          VERIFY_ERROR (INVALID, meta_->get(keyX)); // now unaccessible
         }
       
       

From 7ea9afd1bba6cab967d68c6e9bc9832e2a22850a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 9 Oct 2011 04:20:56 +0200
Subject: [PATCH 091/296] first attempt to implement buffer metadata...

...leading to refactoring
---
 src/proc/engine/buffer-metadata.hpp           | 228 +++++++++---------
 .../proc/engine/buffer-metadata-test.cpp      |   6 +-
 2 files changed, 117 insertions(+), 117 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index e3a071259..6785e1006 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -78,6 +78,8 @@ namespace engine {
       BLOCKED
     };
   
+  
+  
   const LocalKey UNSPECIFIC = 0;
   
   struct TypeHandler
@@ -101,134 +103,54 @@ namespace engine {
     };
   
   const TypeHandler RAW_BUFFER;
+
+
+    /* === Implementation === */
   
-  
-  class Metadata
-    : boost::noncopyable
-    {
-    public:
-      class Entry
-        {
-          HashVal parent_;
-          
-        protected:
-          Entry (HashVal parent) : parent_(parent) { }
-         ~Entry ()                                 { }
-          
-        public:
-          HashVal parentKey()  const { return parent_;}
-          
-          virtual BufferState state()  const              =0;
-          virtual const void* access() const              =0;
-          virtual Entry& mark (BufferState newState)      =0;
-        };
-      
-      
-      Metadata (Literal implementationID)
-        { }
-      
-      HashVal
-      key ( size_t storageSize
-          , TypeHandler instanceFunc =RAW_BUFFER
-          , LocalKey specifics =UNSPECIFIC)
-        {
-          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
-        }
-      
-      HashVal
-      key (HashVal parentKey, TypeHandler instanceFunc)
-        {
-          UNIMPLEMENTED ("create sub-type key");
-        }
-      
-      HashVal
-      key (HashVal parentKey, LocalKey specifics)
-        {
-          UNIMPLEMENTED ("create sub-type key");
-        }
-      
-      HashVal
-      key (HashVal parentKey, const void* concreteBuffer)
-        {
-          UNIMPLEMENTED ("create sub-object key for concrete buffer");
-        }
-      
-      Entry&
-      get (HashVal key)
-        {
-          UNIMPLEMENTED ("access, possibly create metadata records");
-        }
-      
-      bool
-      isKnown (HashVal key)  const
-        {
-          UNIMPLEMENTED ("diagnostics: known record?");
-        }
-      
-      bool
-      isLocked (HashVal key)  const
-        {
-          UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
-        }
-      
-      
-      /* == memory management == */
-      
-      Entry& markLocked (HashVal parentKey, const void* buffer);
-      void release (HashVal key);
-      
-    };
-    
-    
-    
-    
-  /* === Implementation === */
-  
-  namespace {
+  namespace metadata {
     
     using error::LUMIERA_ERROR_LIFECYCLE;
     using error::LUMIERA_ERROR_BOTTOM_VALUE;
     
     
-    class TypeEntry
-      : public Metadata::Entry
+    class Key
       {
-        virtual BufferState
-        state()  const
-          {
-            return NIL;
-          }
+        HashVal parent_;
+        HashVal hashID_;
         
-        virtual const void*
-        access()  const
-          {
-            throw error::Logic ("This metadata entry is still abstract. "
-                                "It can't be associated with a concrete buffer. "
-                                "You need to invoke markLocked (buffer)."
-                               , LUMIERA_ERROR_LIFECYCLE
-                               );
-          }
+        size_t storageSize_;
+        TypeHandler instanceFunc_;
+        LocalKey specifics_;
         
-        virtual Entry&
-        mark (BufferState newState)
-          {
-            if (newState != NIL)
-              throw error::Logic ("This metadata entry is still abstract. "
-                                  "The only possible state transition is to markLocked (buffer)."
-                                 , LUMIERA_ERROR_LIFECYCLE
-                                 );
-            return *this;
-          }
+      public:
+        Key (HashVal familyID, size_t storageSize)
+          : parent_(0)
+          , hashID_(familyID)
+          , storageSize_(storageSize)
+          , instanceFunc_(RAW_BUFFER)
+          , specifics_(UNSPECIFIC)
+          { }
         
+        Key (Key const& parent) 
+          : parent_(parent.hashID_)
+          , hashID_(0)
+          , storageSize_(parent.storageSize_)
+          , instanceFunc_(parent.instanceFunc_)
+          , specifics_(parent.specifics_)
+          { }
+        
+        HashVal parentKey()  const { return parent_;}
+        operator HashVal()   const { return hashID_;}
       };
     
     
-    class BufferEntry
-      : public Metadata::Entry
+    class Entry
+      : public Key
       {
         BufferState state_;
         const void* buffer_;
         
+      public:
         virtual BufferState
         state()  const
           {
@@ -297,6 +219,84 @@ namespace engine {
   
   
   
+  
+  class Metadata
+    : boost::noncopyable
+    {
+    public:
+      
+      typedef metadata::Key Key;
+      typedef metadata::Entry Entry;
+      
+      
+      Metadata (Literal implementationID)
+        { }
+      
+      Key
+      key ( size_t storageSize
+          , TypeHandler instanceFunc =RAW_BUFFER
+          , LocalKey specifics =UNSPECIFIC)
+        {
+          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
+        }
+      
+      Key
+      key (HashVal parentKey, TypeHandler instanceFunc)
+        {
+          UNIMPLEMENTED ("create sub-type key");
+        }
+      
+      Key
+      key (HashVal parentKey, LocalKey specifics)
+        {
+          UNIMPLEMENTED ("create sub-type key");
+        }
+      
+      Key
+      key (HashVal parentKey, const void* concreteBuffer)
+        {
+          UNIMPLEMENTED ("create sub-object key for concrete buffer");
+        }
+      
+      Key const&
+      get (HashVal hashID)
+        {
+          UNIMPLEMENTED ("access the plain key entry");
+        }
+      
+      Entry&
+      get (Key key)
+        {
+          UNIMPLEMENTED ("access, possibly create metadata records");
+        }
+      
+      bool
+      isKnown (HashVal key)  const
+        {
+          UNIMPLEMENTED ("diagnostics: known record?");
+        }
+      
+      bool
+      isLocked (HashVal key)  const
+        {
+          UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
+        }
+      
+      
+      /* == memory management == */
+      
+      Entry& markLocked (HashVal parentKey, const void* buffer);
+      void release (HashVal key);
+      
+    };
+    
+    
+    
+    
+  
+  
+  
+  
   /** */
   Metadata::Entry&
   Metadata::markLocked (HashVal parentKey, const void* buffer)
@@ -304,13 +304,13 @@ namespace engine {
     UNIMPLEMENTED ("transition to locked state");
     if (!buffer)
       throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
-                         , LUMIERA_ERROR_BOTTOM_VALUE);
+                         , error::LUMIERA_ERROR_BOTTOM_VALUE);
     
-    HashVal newKey = this->key (parentKey, buffer);
+    Key newKey = this->key (parentKey, buffer);
     if (isLocked(newKey))
       throw error::Logic ("Attempt to lock a slot for a new buffer, "
                           "while actually the old buffer is still locked."
-                         , LUMIERA_ERROR_LIFECYCLE );
+                         , error::LUMIERA_ERROR_LIFECYCLE );
     
     return this->get(newKey);
   }
diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp
index fd55bf4c8..de62e39a3 100644
--- a/tests/components/proc/engine/buffer-metadata-test.cpp
+++ b/tests/components/proc/engine/buffer-metadata-test.cpp
@@ -110,11 +110,11 @@ namespace test  {
       void
       verifyBasicProperties()
         {
-          HashVal key = meta_->key(SIZE_A);
+          Metadata::Key key = meta_->key(SIZE_A);
           CHECK (key);
           
-          HashVal key1 = meta_->key(SIZE_A);
-          HashVal key2 = meta_->key(SIZE_B);
+          Metadata::Key key1 = meta_->key(SIZE_A);
+          Metadata::Key key2 = meta_->key(SIZE_B);
           CHECK (key1);
           CHECK (key2);
           CHECK (key == key1);

From d9f64c94bf5353033db6bf588ebe23550941b10b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 9 Oct 2011 14:52:58 +0200
Subject: [PATCH 092/296] simple demo using a pointer and a struct

---
 src/tool/try.cpp | 67 +++++++++++++++++++++++++-----------------------
 1 file changed, 35 insertions(+), 32 deletions(-)

diff --git a/src/tool/try.cpp b/src/tool/try.cpp
index bdfca7100..9e68a90a5 100644
--- a/src/tool/try.cpp
+++ b/src/tool/try.cpp
@@ -19,52 +19,55 @@
 // 1/11  - exploring numeric limits
 // 1/11  - integer floor and wrap operation(s)
 // 1/11  - how to fetch the path of the own executable -- at least under Linux?
-
-
-#include 
+// 10/11 - simple demo using a pointer and a struct
 
 
 #include 
-#include 
 
-extern "C" {
-#include 
-}
-#include "lib/error.hpp"
-#include "lib/symbol.hpp"
-
-
-using std::string;
 using std::cout;
-using std::endl;
-using lib::Literal;
-using lib::STRING_MAX_RELEVANT;
 
-namespace error = lumiera::error;
 
-Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe");
 
-string
-catchMyself ()
-{
-  string buff(STRING_MAX_RELEVANT+1, '\0' );
-  ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT);
-  
-  if (0 > chars_read || chars_read == ssize_t(STRING_MAX_RELEVANT))
-    throw error::Fatal ("unable to discover path of running executable");
-  
-  buff.resize(chars_read);
-  return buff;
-}
+/**
+ * custom datastructure
+ * holding a constant char array with "hey"
+ */
+struct MyStruct
+  {
+    char data_[3];
+    const int length_;
+    
+    MyStruct()
+      : length_(3)
+      { 
+        const char *tmp = "hey";
+        for (int i=0; ilength_; ++i)
+      cout << myPointer->data_[i];
+    
+    cout << "\n";
+  }
+
 
 
 int 
 main (int, char**) //(int argc, char* argv[])
   {
+    printMyStruct (&theStruct);
     
-    NOBUG_INIT;
-    
-    cout << "\n\nwho am I? :" << catchMyself();
     cout <<  "\n.gulp.\n";
     
     return 0;

From 2477f2e682dcb527bb18a9c119f64b4ccf8f920a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 9 Oct 2011 16:03:51 +0200
Subject: [PATCH 093/296] outline of building a hierarchy of hash keys for
 buffer metadata

---
 src/proc/asset/entry-id.hpp         |  1 +
 src/proc/engine/buffer-metadata.hpp | 85 ++++++++++++++++++++++++++---
 2 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp
index 0d51e7fa2..c8b6849ac 100644
--- a/src/proc/asset/entry-id.hpp
+++ b/src/proc/asset/entry-id.hpp
@@ -48,6 +48,7 @@
 #include "lib/hash-indexed.hpp"
 #include "lib/util.hpp"
 
+#include 
 #include 
 #include 
 #include 
diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index 6785e1006..a257f6a8c 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -57,6 +57,7 @@
 #include "lib/error.hpp"
 #include "lib/symbol.hpp"
 
+#include 
 #include 
 
 
@@ -67,7 +68,6 @@ namespace engine {
   namespace error = lumiera::error;
   
   
-  typedef uint64_t LocalKey;
   typedef size_t HashVal;
   
   enum BufferState
@@ -79,8 +79,27 @@ namespace engine {
     };
   
   
+  /**
+   * an opaque ID to be used by the BufferProvider implementation.
+   * Typically this will be used, to set apart some pre-registered
+   * kinds of buffers. It is treated as being part of the buffer type.
+   * LocalKey objects may be copied but not re-assigned or changed.
+   */
+  class LocalKey
+    {
+      uint64_t privateID_;
+      
+      /** assignment prohibited */
+      LocalKey& operator= (LocalKey const&);
+      
+    public:
+      LocalKey (uint64_t opaqueValue=0)
+        : privateID_(opaqueValue)
+        { }
+      
+      operator uint64_t()  const { return privateID_; }
+    };
   
-  const LocalKey UNSPECIFIC = 0;
   
   struct TypeHandler
     {
@@ -102,7 +121,11 @@ namespace engine {
         { }
     };
   
-  const TypeHandler RAW_BUFFER;
+  namespace { // internal constants to mark the default case
+    
+    const LocalKey UNSPECIFIC;
+    const TypeHandler RAW_BUFFER;
+  }
 
 
     /* === Implementation === */
@@ -112,6 +135,14 @@ namespace engine {
     using error::LUMIERA_ERROR_LIFECYCLE;
     using error::LUMIERA_ERROR_BOTTOM_VALUE;
     
+    namespace { // details of hash calculation
+        template
+        HashVal
+        chainedHash(HashVal accumulatedHash, VAL changedValue)
+        {
+          UNIMPLEMENTED ("calculate a new hash value, based on parent hash");
+        }
+    }
     
     class Key
       {
@@ -122,23 +153,63 @@ namespace engine {
         TypeHandler instanceFunc_;
         LocalKey specifics_;
         
+        
       public:
+        /** build a standard basic key describing a kind of Buffer. 
+         * @param familyID basic hash seed value to distinguish
+         *                 families of buffer types managed by
+         *                 different BufferProvider instances
+         * @param storageSize fundamental info: buffer size
+         */
         Key (HashVal familyID, size_t storageSize)
-          : parent_(0)
+          : parent_(chainedHash (familyID, storageSize))
           , hashID_(familyID)
           , storageSize_(storageSize)
           , instanceFunc_(RAW_BUFFER)
           , specifics_(UNSPECIFIC)
           { }
         
-        Key (Key const& parent) 
+        // standard copy operations permitted
+        
+        /** create a derived buffer type description.
+         *  Using a different storage size than the parent type,
+         *  all else remaining the same
+         */
+        Key (Key const& parent, size_t differingStorageSize) 
           : parent_(parent.hashID_)
-          , hashID_(0)
-          , storageSize_(parent.storageSize_)
+          , hashID_(chainedHash (parent_, differingStorageSize))
+          , storageSize_(differingStorageSize)  // differing from parent
           , instanceFunc_(parent.instanceFunc_)
           , specifics_(parent.specifics_)
           { }
         
+        
+        /** create a derived buffer type description.
+         *  Using different ctor and dtor functions,
+         *  all else remaining the same as with parent
+         */
+        Key (Key const& parent, TypeHandler differingTypeHandlerFunctions) 
+          : parent_(parent.hashID_)
+          , hashID_(chainedHash (parent_, differingTypeHandlerFunctions))
+          , storageSize_(parent.storageSize_)
+          , instanceFunc_(differingTypeHandlerFunctions)  // differing from parent
+          , specifics_(parent.specifics_)
+          { }
+        
+        
+        /** create a derived buffer type description.
+         *  Using a different private ID than the parent type,
+         *  all else remaining the same
+         */
+        Key (Key const& parent, LocalKey anotherTypeSpecificInternalID) 
+          : parent_(parent.hashID_)
+          , hashID_(chainedHash (parent_, anotherTypeSpecificInternalID))
+          , storageSize_(parent.storageSize_)
+          , instanceFunc_(parent.instanceFunc_)
+          , specifics_(anotherTypeSpecificInternalID)  // differing from parent
+          { }
+        
+        
         HashVal parentKey()  const { return parent_;}
         operator HashVal()   const { return hashID_;}
       };

From 357bfaa674443e5cdd957b1521cdb11c2d2d1075 Mon Sep 17 00:00:00 2001
From: "Michael R. Fisher" 
Date: Sun, 9 Oct 2011 12:48:47 -0500
Subject: [PATCH 094/296] Added pointer to TimelineViewWindow in
 TimelineZoomScroll

---
 src/gui/panels/timeline-panel.cpp                | 13 +++++++++++--
 src/gui/widgets/timeline/timeline-zoom-scale.cpp |  5 +++++
 src/gui/widgets/timeline/timeline-zoom-scale.hpp |  5 +++++
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index 39c4f5b26..8979db727 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -23,6 +23,7 @@
 
 #include "gui/gtk-lumiera.hpp"
 #include "gui/panels/timeline-panel.hpp"
+#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
 
 #include "gui/workspace/workspace-window.hpp"
 #include "gui/model/project.hpp"
@@ -34,6 +35,7 @@
 using namespace Gtk;
 using namespace sigc;
 using namespace gui::widgets;
+using namespace gui::widgets::timeline;
 using namespace gui::model;
 
 using boost::shared_ptr;  ///////////////////////////////TICKET #796
@@ -131,7 +133,15 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
     = *get_project().get_sequences().begin();  
   timelineWidget.reset(new TimelineWidget(load_state(sequence)));
   pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
-  
+
+  // TimelineWidget is now initialized, lets set it in the zoomScale
+  // and wire it with the timeline state changed signal
+  zoomScale.set_view_window(timelineWidget->get_state()->get_view_window());
+ /* This doesn't work
+  timelineWidget->signal_state_changed().
+      connect(sigc::mem_fun(zoomScale, &TimelineZoomScale::on_timeline_state_changed));
+  */
+
   // Set the initial UI state
   update_sequence_chooser();
   update_tool_buttons();
@@ -139,7 +149,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   show_time (Time::ZERO);
 }
 
-
 const char*
 TimelinePanel::get_title()
 {
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index 9129a5ce5..d71220f7c 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -116,6 +116,11 @@ TimelineZoomScale::signal_zoom()
   return zoomSignal;
 }
 
+void
+TimelineZoomScale::set_view_window(TimelineViewWindow &view_window)
+{
+  timelineViewWindow =& view_window;
+}
 int64_t
 TimelineZoomScale::calculate_zoom_scale()
 {
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
index ebeaaffca..512bea9d8 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
@@ -28,6 +28,7 @@
 
 #include "gui/gtk-lumiera.hpp"
 #include "gui/widgets/mini-button.hpp"
+#include "gui/widgets/timeline/timeline-view-window.hpp"
 
 using namespace Gtk;
 using namespace gui::widgets;
@@ -50,6 +51,8 @@ public:
    */
   sigc::signal signal_zoom();
 
+  void set_view_window(TimelineViewWindow &view_window);
+
 private:
   /* Event Handlers */
 
@@ -90,6 +93,8 @@ protected:
 
   const double smoothing_factor;
   const double button_step_size;
+
+  TimelineViewWindow *timelineViewWindow;
 };
 
 } // namespace gui

From 5905fdf0cb467d6c86773c0f3b2afa0c0a54b138 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 10 Oct 2011 00:08:50 +0200
Subject: [PATCH 095/296] change definition of TimelineStateChangeSignal

the timeline-state-change signal now delivers
the new TimelineState object immediately, instead
of requiring the listeners to pick it up
---
 src/gui/panels/timeline-panel.cpp             |  6 ++---
 src/gui/widgets/timeline-widget.cpp           |  4 ++--
 src/gui/widgets/timeline-widget.hpp           | 13 +++++-----
 src/gui/widgets/timeline/timeline-body.cpp    | 15 +++++++++---
 src/gui/widgets/timeline/timeline-body.hpp    | 10 +++++---
 src/gui/widgets/timeline/timeline-ruler.cpp   | 24 ++++++++++++-------
 src/gui/widgets/timeline/timeline-ruler.hpp   | 19 +++++++++++----
 .../widgets/timeline/timeline-zoom-scale.cpp  | 13 ++++++++++
 .../widgets/timeline/timeline-zoom-scale.hpp  |  9 +++++++
 9 files changed, 83 insertions(+), 30 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index 8979db727..b92554341 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -137,10 +137,8 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   // TimelineWidget is now initialized, lets set it in the zoomScale
   // and wire it with the timeline state changed signal
   zoomScale.set_view_window(timelineWidget->get_state()->get_view_window());
- /* This doesn't work
-  timelineWidget->signal_state_changed().
-      connect(sigc::mem_fun(zoomScale, &TimelineZoomScale::on_timeline_state_changed));
-  */
+  zoomScale.wireTimelineState (timelineWidget->state_changed_signal());
+  
 
   // Set the initial UI state
   update_sequence_chooser();
diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp
index cd1459cd8..038af15b5 100644
--- a/src/gui/widgets/timeline-widget.cpp
+++ b/src/gui/widgets/timeline-widget.cpp
@@ -125,7 +125,7 @@ TimelineWidget::set_state(shared_ptr new_state)
   update_tracks();
   
   // Send the state changed signal
-  stateChangedSignal.emit();
+  stateChangedSignal.emit (state);
 }
 
 void
@@ -178,7 +178,7 @@ TimelineWidget::hovering_track_changed_signal() const
   return hoveringTrackChangedSignal;
 }
 
-sigc::signal
+TimelineWidget::TimelineStateChangeSignal
 TimelineWidget::state_changed_signal() const
 {
   return stateChangedSignal;
diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp
index b8371534b..b3f801f9a 100644
--- a/src/gui/widgets/timeline-widget.hpp
+++ b/src/gui/widgets/timeline-widget.hpp
@@ -115,14 +115,16 @@ public:
   
 public:
   /* ===== Signals ===== */
+  typedef sigc::signal > TimelineStateChangeSignal;
+  typedef sigc::signal > HoveringTrackChangedSignal;
+  
   sigc::signal mouse_hover_signal() const;
   
   sigc::signal playback_period_drag_released_signal() const;
   
-  sigc::signal >
-    hovering_track_changed_signal() const;
+  HoveringTrackChangedSignal hovering_track_changed_signal() const;
     
-  sigc::signal state_changed_signal() const;
+  TimelineStateChangeSignal state_changed_signal() const;
   
   /* ===== Events ===== */
 protected:
@@ -275,9 +277,8 @@ protected:
   // Signals
   sigc::signal mouseHoverSignal;
   sigc::signal playbackPeriodDragReleasedSignal;
-  sigc::signal >
-    hoveringTrackChangedSignal;
-  sigc::signal stateChangedSignal;
+  HoveringTrackChangedSignal hoveringTrackChangedSignal;
+  TimelineStateChangeSignal stateChangedSignal;
     
   bool update_tracks_frozen;
    
diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp
index b0818653a..74fed06f7 100644
--- a/src/gui/widgets/timeline/timeline-body.cpp
+++ b/src/gui/widgets/timeline/timeline-body.cpp
@@ -39,6 +39,8 @@ using namespace lumiera;
 
 using gui::util::CairoUtil;
 
+using boost::shared_ptr;           ////////////////////TICKET #796
+
 namespace gui {
 namespace widgets {
 namespace timeline {
@@ -61,7 +63,7 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget)
   register_styles();
   
   // Reset the state
-  on_state_changed();
+  propagateStateChange();
 }
 
 TimelineBody::~TimelineBody()
@@ -294,9 +296,16 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
 }
 
 void
-TimelineBody::on_state_changed()
+TimelineBody::on_state_changed (shared_ptr newState)
 {
-  if(timelineWidget.get_state())
+  REQUIRE (newState);
+  timelineState = newState;
+}
+
+void
+TimelineBody::propagateStateChange()
+{
+  if (timelineState)
     {
       // Connect up some events
       view_window().changed_signal().connect(
diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp
index 74b057738..f2441da9f 100644
--- a/src/gui/widgets/timeline/timeline-body.hpp
+++ b/src/gui/widgets/timeline/timeline-body.hpp
@@ -125,10 +125,9 @@ protected:
   bool on_motion_notify_event(GdkEventMotion *event);
   
   /**
-   * The event handler for when the TimelineWidget's state object is
-   * replaced.
+   * The event handler for when the TimelineWidget's state is switched.
    */
-  void on_state_changed();
+  void on_state_changed (boost::shared_ptr newState);
   
   /* ===== Internals ===== */
 private:
@@ -161,6 +160,9 @@ private:
   
   void set_vertical_offset(int offset);
   
+  /** adjust to the new timeline state */
+  void propagateStateChange();
+  
   /**
    * A helper function to get the view window
    * @remarks This function must not be called unless the TimlineWidget
@@ -202,6 +204,8 @@ private:
   Cairo::RefPtr playbackPointColour;
   
   gui::widgets::TimelineWidget &timelineWidget;
+  boost::shared_ptr timelineState;      ////////////////////TICKET #796 : should use std::tr1::shared_ptr
+  
 
   friend class Tool;
   friend class ArrowTool;
diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp
index afc395710..650c37912 100644
--- a/src/gui/widgets/timeline/timeline-ruler.cpp
+++ b/src/gui/widgets/timeline/timeline-ruler.cpp
@@ -219,19 +219,27 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation)
 }
 
 void
-TimelineRuler::on_state_changed()
+TimelineRuler::on_state_changed (shared_ptr newState)
 {
-  if(timelineWidget.get_state())
-    {
-      // Connect up some events
-      view_window().changed_signal().connect(
-        sigc::mem_fun(this, &TimelineRuler::on_update_view) );
-    }
-    
+  REQUIRE (newState);
+  timelineState = newState;
+  
+  propagateStateChange();
+}
+
+void
+TimelineRuler::propagateStateChange()
+{
+  // Connect up some events
+  view_window().changed_signal().connect(
+    sigc::mem_fun(this, &TimelineRuler::on_update_view) );
+  
   // Redraw
   on_update_view();
 }
 
+
+
 void
 TimelineRuler::set_leading_x(const int x)
 {
diff --git a/src/gui/widgets/timeline/timeline-ruler.hpp b/src/gui/widgets/timeline/timeline-ruler.hpp
index a7c9b0533..1c2cfbb74 100644
--- a/src/gui/widgets/timeline/timeline-ruler.hpp
+++ b/src/gui/widgets/timeline/timeline-ruler.hpp
@@ -29,6 +29,7 @@
 
 #include "gui/gtk-lumiera.hpp"
 #include "lib/time/timevalue.hpp"
+#include "gui/widgets/timeline/timeline-state.hpp"
 
 namespace gui {
 namespace widgets {
@@ -108,10 +109,9 @@ private:
   void on_size_allocate(Gtk::Allocation& allocation);
   
   /**
-   * The event handler for when the TimelineWidget's state object is
-   * replaced.
+   * The event handler for when the TimelineWidget's state is switched.
    */
-  void on_state_changed();
+  void on_state_changed (boost::shared_ptr newState);         ////////////////////TICKET #796 : should use std::tr1::shared_ptr
   
 private:
   /* ===== Internal Methods ===== */
@@ -161,7 +161,13 @@ private:
    */
   void draw_playback_point(Cairo::RefPtr cr,
     const Gdk::Rectangle ruler_rect);
-
+  
+  /**
+   * After notification of a timeline state switch
+   * do any local adjustments to adapt to the new state 
+   */
+  void propagateStateChange();
+  
   /**
    * Given the current zoom, this function calculates the preiod
    * between major graduations on the ruler scale.
@@ -233,6 +239,11 @@ private:
    */
   gui::widgets::TimelineWidget &timelineWidget;
   
+  /**
+   * the currently active timeline state object 
+   */
+  boost::shared_ptr timelineState;      ////////////////////TICKET #796 : should use std::tr1::shared_ptr
+  
   /**
    * The caches image of the ruler, over which the chevrons etc. will
    * be drawn.
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index d71220f7c..7cb6c171f 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -90,6 +90,19 @@ TimelineZoomScale::TimelineZoomScale()
   show_all();
 }
 
+void
+TimelineZoomScale::wireTimelineState (TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
+{
+  stateChangeSignal.connect (sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed));
+}
+
+void
+TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr newState)
+{
+  REQUIRE (newState);
+  UNIMPLEMENTED ("react on the timeline state change");
+}
+
 void
 TimelineZoomScale::on_zoom_in_clicked()
 {
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
index 512bea9d8..1514e8b77 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
@@ -28,8 +28,11 @@
 
 #include "gui/gtk-lumiera.hpp"
 #include "gui/widgets/mini-button.hpp"
+#include "gui/widgets/timeline-widget.hpp"
 #include "gui/widgets/timeline/timeline-view-window.hpp"
 
+#include 
+
 using namespace Gtk;
 using namespace gui::widgets;
 
@@ -52,9 +55,15 @@ public:
   sigc::signal signal_zoom();
 
   void set_view_window(TimelineViewWindow &view_window);
+  void wireTimelineState (TimelineWidget::TimelineStateChangeSignal);
 
 private:
   /* Event Handlers */
+  
+  /**
+   * 
+   */
+  void on_timeline_state_changed (boost::shared_ptr newState);         ////////////////////TICKET #796 : should use std::tr1::shared_ptr
 
   /**
    * Event handler for when the zoomIn Button

From 5fe1debd5beeefb8dfc671bdea9596cf49daec02 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 10 Oct 2011 01:07:10 +0200
Subject: [PATCH 096/296] ZoomScale: rely on the state change signal for the
 view window

---
 src/gui/panels/timeline-panel.cpp               | 12 ++++++------
 src/gui/panels/timeline-panel.hpp               |  2 +-
 .../widgets/timeline/timeline-zoom-scale.cpp    | 17 ++++++++++++-----
 .../widgets/timeline/timeline-zoom-scale.hpp    | 12 ++++++++----
 4 files changed, 27 insertions(+), 16 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index b92554341..970aa21d4 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -112,7 +112,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   panelBar.pack_start(toolbar, PACK_SHRINK);
 
   // Setup tooltips
-  sequenceChooser     .set_tooltip_text(_("Change sequence"));
+  sequenceChooser .set_tooltip_text(_("Change sequence"));
 
   previousButton  .set_tooltip_text(_("To beginning"));
   rewindButton    .set_tooltip_text(_("Rewind"));
@@ -129,15 +129,15 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   zoomScale       .set_tooltip_text(_("Adjust timeline zoom scale"));
 
   // Setup the timeline widget
-  shared_ptr sequence          ///////////////////////////////TICKET #796
+  shared_ptr sequence          ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr instead of boost
     = *get_project().get_sequences().begin();  
   timelineWidget.reset(new TimelineWidget(load_state(sequence)));
   pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
 
-  // TimelineWidget is now initialized, lets set it in the zoomScale
-  // and wire it with the timeline state changed signal
-  zoomScale.set_view_window(timelineWidget->get_state()->get_view_window());
-  zoomScale.wireTimelineState (timelineWidget->state_changed_signal());
+  // since TimelineWidget is now initialised,
+  // wire the zoom slider to react on timeline state changes
+  zoomScale.wireTimelineState (timelineWidget->get_state(),
+                               timelineWidget->state_changed_signal());
   
 
   // Set the initial UI state
diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp
index aac000277..8b165dd79 100644
--- a/src/gui/panels/timeline-panel.hpp
+++ b/src/gui/panels/timeline-panel.hpp
@@ -174,7 +174,7 @@ private:
   boost::scoped_ptr timelineWidget;
   
   std::map< boost::weak_ptr,
-    boost::shared_ptr >          ///////////////////////////////TICKET #796
+    boost::shared_ptr >          ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr
     timelineStates;
   
   // Toolbar Widgets
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index 7cb6c171f..8d084b525 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -91,8 +91,10 @@ TimelineZoomScale::TimelineZoomScale()
 }
 
 void
-TimelineZoomScale::wireTimelineState (TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
+TimelineZoomScale::wireTimelineState (boost::shared_ptr currentState,
+                                      TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
 {
+  on_timeline_state_changed (currentState);
   stateChangeSignal.connect (sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed));
 }
 
@@ -100,7 +102,9 @@ void
 TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr newState)
 {
   REQUIRE (newState);
-  UNIMPLEMENTED ("react on the timeline state change");
+  timelineState = newState;
+  
+  UNIMPLEMENTED ("react on the timeline state change");  ///////////////////////////TODO
 }
 
 void
@@ -129,11 +133,14 @@ TimelineZoomScale::signal_zoom()
   return zoomSignal;
 }
 
-void
-TimelineZoomScale::set_view_window(TimelineViewWindow &view_window)
+TimelineViewWindow&
+TimelineZoomScale::getViewWindow()
 {
-  timelineViewWindow =& view_window;
+  REQUIRE (timelineState, "lifecycle error");
+  return timelineState->get_view_window();
 }
+
+
 int64_t
 TimelineZoomScale::calculate_zoom_scale()
 {
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
index 1514e8b77..9bf5dde9f 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
@@ -29,6 +29,7 @@
 #include "gui/gtk-lumiera.hpp"
 #include "gui/widgets/mini-button.hpp"
 #include "gui/widgets/timeline-widget.hpp"
+#include "gui/widgets/timeline/timeline-state.hpp"
 #include "gui/widgets/timeline/timeline-view-window.hpp"
 
 #include 
@@ -54,8 +55,8 @@ public:
    */
   sigc::signal signal_zoom();
 
-  void set_view_window(TimelineViewWindow &view_window);
-  void wireTimelineState (TimelineWidget::TimelineStateChangeSignal);
+  void wireTimelineState (boost::shared_ptr currentState,
+                          TimelineWidget::TimelineStateChangeSignal);
 
 private:
   /* Event Handlers */
@@ -83,6 +84,9 @@ private:
    */
   void on_zoom();
 
+  /** access current timeline state */
+  TimelineViewWindow& getViewWindow();
+
   /**
    * Calculate a Zoom Scale value based on
    * the adjustment's current value
@@ -96,14 +100,14 @@ private:
   MiniButton zoomIn;
   MiniButton zoomOut;
 
-protected:
+private:
   /* Signals */
   sigc::signal zoomSignal;
 
   const double smoothing_factor;
   const double button_step_size;
 
-  TimelineViewWindow *timelineViewWindow;
+  boost::shared_ptr timelineState;
 };
 
 } // namespace gui

From fb28592082dc8917e19af7f025a3fd734e15f15b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 10 Oct 2011 01:42:03 +0200
Subject: [PATCH 097/296] more cleanup: use the propagated timelineState
 directly

---
 src/gui/widgets/timeline/timeline-body.cpp  | 54 ++++++++---------
 src/gui/widgets/timeline/timeline-body.hpp  | 20 +++---
 src/gui/widgets/timeline/timeline-ruler.cpp | 67 ++++++++++-----------
 src/gui/widgets/timeline/timeline-ruler.hpp |  4 +-
 4 files changed, 65 insertions(+), 80 deletions(-)

diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp
index 74fed06f7..3bee2110f 100644
--- a/src/gui/widgets/timeline/timeline-body.cpp
+++ b/src/gui/widgets/timeline/timeline-body.cpp
@@ -71,6 +71,13 @@ TimelineBody::~TimelineBody()
   WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here");
 }
 
+TimelineViewWindow&
+TimelineBody::viewWindow() const
+{
+  REQUIRE(timelineState);
+  return timelineState->get_view_window();
+}
+
 TimelineWidget&
 TimelineBody::getTimelineWidget () const
 {
@@ -149,7 +156,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event)
   // Makes sure the widget styles have been loaded
   read_styles();
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       // Prepare to render via cairo
       const Allocation allocation = get_allocation();
@@ -171,9 +178,9 @@ TimelineBody::on_scroll_event (GdkEventScroll* event)
 {
   REQUIRE(event != NULL);
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
-      TimelineViewWindow &window = view_window();
+      TimelineViewWindow &window = viewWindow();
       const Allocation allocation = get_allocation();
       
       if(event->state & GDK_CONTROL_MASK)
@@ -257,17 +264,16 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
 {
   REQUIRE(event != NULL);
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       // Handle a middle-mouse drag if one is occuring
       switch(dragType)
         {
         case Shift:
           {
-            TimelineViewWindow &window = view_window();
-            
                                    /////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view!
                                    /////////////////////////////            : either encapsulate this entirely here, or leave it to the timeline view!            
+            TimelineViewWindow &window = viewWindow();
             const int64_t scale = window.get_time_scale();
             window.set_time_offset(beginShiftTimeOffset
                                  + TimeValue(scale * (mouseDownX - event->x)));
@@ -308,7 +314,7 @@ TimelineBody::propagateStateChange()
   if (timelineState)
     {
       // Connect up some events
-      view_window().changed_signal().connect(
+      viewWindow().changed_signal().connect(
         sigc::mem_fun(this, &TimelineBody::on_update_view) );
     }
     
@@ -382,7 +388,7 @@ TimelineBody::draw_track(Cairo::RefPtr cr,
 
   // Render the track
   cr->save();
-  TimelineViewWindow &window = view_window();
+  TimelineViewWindow &window = viewWindow();
   timeline_track->draw_track(cr, &window);
   cr->restore();
 }
@@ -395,12 +401,11 @@ TimelineBody::draw_selection(Cairo::RefPtr cr)
   // Prepare
   const Allocation allocation = get_allocation();
   
-  shared_ptr state = timelineWidget.get_state();
-  REQUIRE(state);
+  REQUIRE(timelineState);
   
-  TimelineViewWindow const& window = state->get_view_window();
-  const int start_x = window.time_to_x(state->getSelectionStart());
-  const int end_x   = window.time_to_x(state->getSelectionEnd());
+  TimelineViewWindow const& window = timelineState->get_view_window();
+  const int start_x = window.time_to_x(timelineState->getSelectionStart());
+  const int end_x   = window.time_to_x(timelineState->getSelectionEnd());
   
   // Draw the cover
   if(end_x > 0 && start_x < allocation.get_width())
@@ -436,16 +441,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr cr)
 {   
   REQUIRE(cr);
   
-  // Prepare
-  
-  shared_ptr state = timelineWidget.get_state();
-  if(state)
+  if (timelineState)
     {
-      if (!state->isPlaying()) return;
+      if (!timelineState->isPlaying()) return;
       
       const Allocation allocation = get_allocation();
-      Time point = state->getPlaybackPoint();
-      const int x = view_window().time_to_x(point);
+      Time point = timelineState->getPlaybackPoint();
+      const int x = viewWindow().time_to_x(point);
         
       // Set source
       cr->set_source(playbackPointColour);
@@ -464,10 +466,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr cr)
 void
 TimelineBody::begin_shift_drag()
 {
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       dragType = Shift;
-      beginShiftTimeOffset = view_window().get_time_offset();
+      beginShiftTimeOffset = viewWindow().get_time_offset();
       beginShiftVerticalOffset = get_vertical_offset();
     }
 }
@@ -484,14 +486,6 @@ TimelineBody::set_vertical_offset(int offset)
   timelineWidget.verticalAdjustment.set_value(offset);
 }
 
-TimelineViewWindow&
-TimelineBody::view_window() const
-{
-  shared_ptr state = timelineWidget.get_state();
-  REQUIRE(state);
-  return state->get_view_window();
-}
-
 void
 TimelineBody::register_styles() const
 {
diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp
index f2441da9f..aaf90cb2e 100644
--- a/src/gui/widgets/timeline/timeline-body.hpp
+++ b/src/gui/widgets/timeline/timeline-body.hpp
@@ -68,10 +68,7 @@ public:
    */
   TimelineBody(gui::widgets::TimelineWidget &timeline_widget);
   
-  /**
-   * Destructor
-   */
-  ~TimelineBody();
+  virtual ~TimelineBody();
   
   TimelineWidget&
   getTimelineWidget () const;
@@ -131,6 +128,12 @@ protected:
   
   /* ===== Internals ===== */
 private:
+  /**
+   * Access the current timeline view window
+   * @warning must not be called unless the TimlineWidget
+   *          has a valid state.
+   */
+  TimelineViewWindow& viewWindow() const;
 
   /**
    * Draws the timeline tracks.
@@ -162,14 +165,7 @@ private:
   
   /** adjust to the new timeline state */
   void propagateStateChange();
-  
-  /**
-   * A helper function to get the view window
-   * @remarks This function must not be called unless the TimlineWidget
-   * has a valid state.
-   */
-  TimelineViewWindow& view_window() const;
-   
+
   /**
    * Registers all the styles that this class will respond to.
    */
diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp
index 650c37912..03a866482 100644
--- a/src/gui/widgets/timeline/timeline-ruler.cpp
+++ b/src/gui/widgets/timeline/timeline-ruler.cpp
@@ -75,6 +75,14 @@ TimelineRuler::TimelineRuler (TimelineWidget &timeline_widget)
   register_styles();
 }
 
+TimelineViewWindow&
+TimelineRuler::viewWindow() const
+{
+  REQUIRE(timelineState);
+  return timelineState->get_view_window();
+}
+
+
 void
 TimelineRuler::set_mouse_chevron_offset(int offset)
 {
@@ -114,7 +122,7 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
   if(!window)
     return false;
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       // Prepare to render via cairo      
       const Allocation allocation = get_allocation();
@@ -160,11 +168,11 @@ TimelineRuler::on_button_press_event(GdkEventButton* event)
 {
   REQUIRE(event != NULL);
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       if(event->button == 1)
       {
-        pinnedDragTime = view_window().x_to_time(event->x);
+        pinnedDragTime = viewWindow().x_to_time(event->x);
         isDragging = true;
       }
     }
@@ -231,7 +239,7 @@ void
 TimelineRuler::propagateStateChange()
 {
   // Connect up some events
-  view_window().changed_signal().connect(
+  viewWindow().changed_signal().connect(
     sigc::mem_fun(this, &TimelineRuler::on_update_view) );
   
   // Redraw
@@ -243,18 +251,16 @@ TimelineRuler::propagateStateChange()
 void
 TimelineRuler::set_leading_x(const int x)
 {
-  shared_ptr state = timelineWidget.get_state();
-  
-  if(state)
+  if (timelineState)
     {
-      TimeVar newStartPoint (view_window().x_to_time(x));
+      TimeVar newStartPoint (viewWindow().x_to_time(x));
       Offset selectionLength (pinnedDragTime, newStartPoint);
       
       if (newStartPoint > pinnedDragTime)
         newStartPoint=pinnedDragTime; // use the smaller one as selection start
       
-      state->setPlaybackPeriod (Mutation::changeTime(newStartPoint)      );
-      state->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
+      timelineState->setPlaybackPeriod (Mutation::changeTime(newStartPoint)      );
+      timelineState->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
                                    //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once
                                                                      ////////////////////TODO        : code duplication with timeline-ibeam-tool 205      
    }
@@ -268,7 +274,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr cr,
   REQUIRE(ruler_rect.get_width() > 0);
   REQUIRE(ruler_rect.get_height() > 0);
   
-  const TimelineViewWindow &window = view_window();
+  const TimelineViewWindow &window = viewWindow();
   const gavl_time_t left_offset = _raw(window.get_time_offset());
   const int64_t time_scale = window.get_time_scale();
   
@@ -369,16 +375,15 @@ TimelineRuler::draw_selection(Cairo::RefPtr cr,
   REQUIRE(cr);
   REQUIRE(ruler_rect.get_width() > 0);
   REQUIRE(ruler_rect.get_height() > 0);
+  REQUIRE(timelineState);
   
-  shared_ptr state = timelineWidget.get_state();
-  REQUIRE(state);
-  const TimelineViewWindow &window = state->get_view_window();
+  const TimelineViewWindow &window = timelineState->get_view_window();
 
   Glib::RefPtr
My TiddlyWiki is loading ...

Requires Javascript.
- - Data Backend - design draft - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #8cf
-PrimaryLight: #18f
-PrimaryMid: #04b
-PrimaryDark: #014
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
/*{{{*/
-body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-
-a {color:[[ColorPalette::PrimaryMid]];}
-a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
-a img {border:0;}
-
-h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
-h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
-h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
-
-.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
-.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
-.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
-
-.header {background:[[ColorPalette::PrimaryMid]];}
-.headerShadow {color:[[ColorPalette::Foreground]];}
-.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
-.headerForeground {color:[[ColorPalette::Background]];}
-.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
-
-.tabSelected{color:[[ColorPalette::PrimaryDark]];
-	background:[[ColorPalette::TertiaryPale]];
-	border-left:1px solid [[ColorPalette::TertiaryLight]];
-	border-top:1px solid [[ColorPalette::TertiaryLight]];
-	border-right:1px solid [[ColorPalette::TertiaryLight]];
-}
-.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
-.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
-.tabContents .button {border:0;}
-
-#sidebar {}
-#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
-#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
-
-.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
-.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
-.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
-	border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
-.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
-.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
-.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
-	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
-.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
-.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
-	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
-
-#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
-#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
-
-.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
-
-.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
-.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
-.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
-.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
-
-.tiddler .defaultCommand {font-weight:bold;}
-
-.shadow .title {color:[[ColorPalette::TertiaryDark]];}
-
-.title {color:[[ColorPalette::SecondaryDark]];}
-.subtitle {color:[[ColorPalette::TertiaryDark]];}
-
-.toolbar {color:[[ColorPalette::PrimaryMid]];}
-.toolbar a {color:[[ColorPalette::TertiaryLight]];}
-.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
-.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
-
-.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
-.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
-.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
-.tagging .button, .tagged .button {border:none;}
-
-.footer {color:[[ColorPalette::TertiaryLight]];}
-.selected .footer {color:[[ColorPalette::TertiaryMid]];}
-
-.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
-.sparktick {background:[[ColorPalette::PrimaryDark]];}
-
-.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
-.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
-.lowlight {background:[[ColorPalette::TertiaryLight]];}
-
-.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
-
-.imageLink, #displayArea .imageLink {background:transparent;}
-
-.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
-
-.viewer .listTitle {list-style-type:none; margin-left:-2em;}
-.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
-.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
-.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
-.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
-.viewer code {color:[[ColorPalette::SecondaryDark]];}
-.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
-
-.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
-
-.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
-.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
-.editorFooter {color:[[ColorPalette::TertiaryMid]];}
-
-#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
-#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
-#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
-#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
-.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
-.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
-#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
-/*}}}*/
-
-
-
/*{{{*/
-* html .tiddler {height:1%;}
-
-body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
-
-h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
-h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
-h4,h5,h6 {margin-top:1em;}
-h1 {font-size:1.35em;}
-h2 {font-size:1.25em;}
-h3 {font-size:1.1em;}
-h4 {font-size:1em;}
-h5 {font-size:.9em;}
-
-hr {height:1px;}
-
-a {text-decoration:none;}
-
-dt {font-weight:bold;}
-
-ol {list-style-type:decimal;}
-ol ol {list-style-type:lower-alpha;}
-ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol {list-style-type:decimal;}
-ol ol ol ol ol {list-style-type:lower-alpha;}
-ol ol ol ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol ol ol ol {list-style-type:decimal;}
-
-.txtOptionInput {width:11em;}
-
-#contentWrapper .chkOptionInput {border:0;}
-
-.externalLink {text-decoration:underline;}
-
-.indent {margin-left:3em;}
-.outdent {margin-left:3em; text-indent:-3em;}
-code.escaped {white-space:nowrap;}
-
-.tiddlyLinkExisting {font-weight:bold;}
-.tiddlyLinkNonExisting {font-style:italic;}
-
-/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
-a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
-
-#mainMenu .tiddlyLinkExisting,
-	#mainMenu .tiddlyLinkNonExisting,
-	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
-#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
-
-.header {position:relative;}
-.header a:hover {background:transparent;}
-.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
-.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
-
-.siteTitle {font-size:3em;}
-.siteSubtitle {font-size:1.2em;}
-
-#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
-
-#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
-#sidebarOptions {padding-top:0.3em;}
-#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
-#sidebarOptions input {margin:0.4em 0.5em;}
-#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
-#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
-#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
-#sidebarTabs .tabContents {width:15em; overflow:hidden;}
-
-.wizard {padding:0.1em 1em 0em 2em;}
-.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizardStep {padding:1em 1em 1em 1em;}
-.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
-.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
-.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
-.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
-
-#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
-.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
-#messageArea a {text-decoration:underline;}
-
-.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
-.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
-
-.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
-.popup .popupMessage {padding:0.4em;}
-.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
-.popup li.disabled {padding:0.4em;}
-.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
-.listBreak {font-size:1px; line-height:1px;}
-.listBreak div {margin:2px 0;}
-
-.tabset {padding:1em 0em 0em 0.5em;}
-.tab {margin:0em 0em 0em 0.25em; padding:2px;}
-.tabContents {padding:0.5em;}
-.tabContents ul, .tabContents ol {margin:0; padding:0;}
-.txtMainTab .tabContents li {list-style:none;}
-.tabContents li.listLink { margin-left:.75em;}
-
-#contentWrapper {display:block;}
-#splashScreen {display:none;}
-
-#displayArea {margin:1em 17em 0em 14em;}
-
-.toolbar {text-align:right; font-size:.9em;}
-
-.tiddler {padding:1em 1em 0em 1em;}
-
-.missing .viewer,.missing .title {font-style:italic;}
-
-.title {font-size:1.6em; font-weight:bold;}
-
-.missing .subtitle {display:none;}
-.subtitle {font-size:1.1em;}
-
-.tiddler .button {padding:0.2em 0.4em;}
-
-.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
-.isTag .tagging {display:block;}
-.tagged {margin:0.5em; float:right;}
-.tagging, .tagged {font-size:0.9em; padding:0.25em;}
-.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
-.tagClear {clear:both;}
-
-.footer {font-size:.9em;}
-.footer li {display:inline;}
-
-.annotation {padding:0.5em; margin:0.5em;}
-
-* html .viewer pre {width:99%; padding:0 0 1em 0;}
-.viewer {line-height:1.4em; padding-top:0.5em;}
-.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
-.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
-.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
-
-.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
-.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
-table.listView {font-size:0.85em; margin:0.8em 1.0em;}
-table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
-
-.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
-.viewer code {font-size:1.2em; line-height:1.4em;}
-
-.editor {font-size:1.1em;}
-.editor input, .editor textarea {display:block; width:100%; font:inherit;}
-.editorFooter {padding:0.25em 0em; font-size:.9em;}
-.editorFooter .button {padding-top:0px; padding-bottom:0px;}
-
-.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
-
-.sparkline {line-height:1em;}
-.sparktick {outline:0;}
-
-.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
-.zoomer div {padding:1em;}
-
-* html #backstage {width:99%;}
-* html #backstageArea {width:99%;}
-#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageToolbar {position:relative;}
-#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
-#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
-#backstage {position:relative; width:100%; z-index:50;}
-#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
-.backstagePanelFooter {padding-top:0.2em; float:right;}
-.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
-#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
-
-.whenBackstage {display:none;}
-.backstageVisible .whenBackstage {display:block;}
-/*}}}*/
-
-
-
/***
-StyleSheet for use when a translation requires any css style changes.
-This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
-***/
-
-/*{{{*/
-body {font-size:0.8em;}
-
-#sidebarOptions {font-size:1.05em;}
-#sidebarOptions a {font-style:normal;}
-#sidebarOptions .sliderPanel {font-size:0.95em;}
-
-.subtitle {font-size:0.8em;}
-
-.viewer table.listView {font-size:0.95em;}
-
-.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
-/*}}}*/
-
-
-
/*{{{*/
-@media print {
-#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
-#displayArea {margin: 1em 1em 0em 1em;}
-/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
-noscript {display:none;}
-}
-/*}}}*/
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-<div class='headerShadow'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-<div class='headerForeground'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-</div>
-<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
-<div id='sidebar'>
-<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-<div id='messageArea'></div>
-<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
-<div class='title' macro='view title'></div>
-<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
-<div class='tagging' macro='tagging'></div>
-<div class='tagged' macro='tags'></div>
-<div class='viewer' macro='view text wikified'></div>
-<div class='tagClear'></div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
-<div class='title' macro='view title'></div>
-<div class='editor' macro='edit title'></div>
-<div macro='annotations'></div>
-<div class='editor' macro='edit text'></div>
-<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
-<!--}}}-->
-
-
-
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
-* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
-* MainMenu: The menu (usually on the left)
-* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
-You'll also need to enter your username for signing your edits: <<option txtUserName>>
-
-
-
These InterfaceOptions for customising TiddlyWiki are saved in your browser
-
-Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
-
-<<option txtUserName>>
-<<option chkSaveBackups>> SaveBackups
-<<option chkAutoSave>> AutoSave
-<<option chkRegExpSearch>> RegExpSearch
-<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
-<<option chkAnimate>> EnableAnimations
-
-----
-Also see AdvancedOptions
-
-
- -
-
-
A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far.  Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:
-<<<
-<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.
-
-Try modifying the time spent.  Suppose you've just spent one more hour and want to record it.  Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window.  Watch the time remaining go down to 1 hour.
-
-In reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours.  The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:
-<<<
-<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-You can adjust the current estimate in the same way as you adjusted the time spent.  Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".
-
-You can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases).  Click on the time remaining and add an hour by typing "+1".
-
-When the time remaining goes to zero, the task is considered complete:
-<<<
-<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you haven't already done so, try double-clicking the description.  Yes, it really does open up the editor and select just the text of the description.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 0.5>> Put tasksum on the ViewTemplate.
-<<<
-You can specify just the description of a task, and leave it unestimated.  Click the question mark to enter the estimate:
-<<<
-<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-As this task implies, you can enter two values in the popup when you click on any of the time cells.  Separate them with spaces and/or a comma.  Experiment:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-Finally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there.  That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.
-
-And setting up a taskadder is simplicity itself.  Just add {{{<<taskadder>>}}} to your tiddler.  You will see this:
-<<<
-<<taskadder>>
-<<<
-Just type a task description into the first field, and your initial estimate for how long it will take into the second field.  Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler.  Notice that you can just start typing a new task as soon as you're done entering the first one.
-
-You can have as many taskadders as you like in any tiddler.  The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse.  Try adding some tasks here and in the above adder:
-<<<
-<<taskadder>>
-<<<
-Notice that the one you just used takes focus when this tiddler is redisplayed.
-
-A taskadder by default adds tasks above itself.  You can make it add them below by adding a {{{below}}} argument to the macro call:
-<<<
-<<taskadder below>>
-<<<
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
In this tutorial, we've been looking mostly at individual tasks.  In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler.  In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.
-
-To get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them.  Here's an example:
-<<<
-<<tasksum start>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end>>
-<<<
-If you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.
-<<<
-<<tasksum start here>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<tasksum end>>
-<<<
-You can nest these things if you like, just be sure to match starts and ends:
-<<<
-<<tasksum start here>>
-* Time cell manipulation:<<tasksum start>>
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<tasksum end "Cell manipulation:">>
-<<br>>
-* Double-click handling:<<tasksum start>>
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end "Double-clicks:">>
-
-<<tasksum end>>
-<<<
-Finally, the simplest way to use tasksum is to add it to your view template.  See TaskSummaryViewTemplate for an example template.  Note that if no tasks are present between the start and end, nothing is displayed.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort.  However, there are two issues that may affect you.  (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)
-# The task macros don't play nicely with the default TiddlyWiki display of tags.  In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler.  However, this little box may interfere with the tables used by the task macros.  In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible.  In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box.  If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative.  Simply import it into your wiki and rename it to ViewTemplate.  This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>
-# Most view templates also add a minus sign ("-") before the "close" command.  TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out.  But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it.  It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed.  So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.
-
-----
-This ends the tutorial.  To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
-
-
-
PageTemplate
-|>|SiteTitle - SiteSubtitle|
-|>|MainMenu|
-|DefaultTiddlers<<br>><<br>><<br>>ViewTemplate<<br>><<br>>EditTemplate|SideBarOptions|
-|~|OptionsPanel|
-|~|SideBarTabs|
-|~|AdvancedOptions|
-|~|<<tiddler Configuration.SideBarTabs>>|
-
-''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
-
-ColorPalette
-
-SiteUrl
-
-
-
/***
-|Name|BetterTimelineMacro|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#BetterTimelineMacro|
-|Version|0.5 beta|
-|Requires|~TW2.x|
-!!!Description:
-A replacement for the core timeline macro that offers more features:
-*list tiddlers with only specfic tag
-*exclude tiddlers with a particular tag
-*limit entries to any number of days, for example one week
-*specify a start date for the timeline, only tiddlers after that date will be listed.
-
-!!!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!!!Syntax:
-{{{<<timeline better:true>>}}}
-''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
-
-additonal params:
-(use only the ones you want)
-{{{<<timeline better:true  onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30>>}}}
-
-''explanation of syntax:''
-onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
-excludeTag: tiddlers with this tag will not be listed.
-sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
-firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
-maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
-maxEntries: limit the total number of entries in the timeline.
-
-
-!!!History:
-*28-07-06: ver 0.5 beta, first release
-
-!!!Code
-***/
-//{{{
-// Return the tiddlers as a sorted array
-TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
-{
-          var results = [];
-          this.forEachTiddler(function(title,tiddler)
-          {
-          if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
-                        if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
-                                      results.push(tiddler);
-          });
-          if(field)
-                   results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
-          return results;
-}
-
-
-
-//this function by Udo
-function getParam(params, name, defaultValue)
-{
-          if (!params)
-          return defaultValue;
-          var p = params[0][name];
-          return p ? p[0] : defaultValue;
-}
-
-window.old_timeline_handler= config.macros.timeline.handler;
-config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
-{
-          var args = paramString.parseParams("list",null,true);
-          var betterMode = getParam(args, "better", "false");
-          if (betterMode == 'true')
-          {
-          var sortBy = getParam(args,"sortBy","modified");
-          var excludeTag = getParam(args,"excludeTag",undefined);
-          var includeTag = getParam(args,"onlyTag",undefined);
-          var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
-          var firstDayParam = getParam(args,"firstDay",undefined);
-          var firstDay = (firstDayParam!=undefined)? firstDayParam: "00010101";
-          var lastDay = "";
-          var field= sortBy;
-          var maxDaysParam = getParam(args,"maxDays",undefined);
-          var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
-          var maxEntries = getParam(args,"maxEntries",undefined);
-          var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
-          for(var t=tiddlers.length-1; t>=last; t--)
-                  {
-                  var tiddler = tiddlers[t];
-                  var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
-                  if ((theDay>=firstDay)&& (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
-                     {
-                     if(theDay != lastDay)
-                               {
-                               var theDateList = document.createElement("ul");
-                               place.appendChild(theDateList);
-                               createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
-                               lastDay = theDay;
-                               }
-                  var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
-                  theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
-                  }
-                  }
-          }
-
-          else
-              {
-              window.old_timeline_handler.apply(this,arguments);
-              }
-}
-//}}}
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #f8a
-PrimaryLight: #f48
-PrimaryMid: #824
-PrimaryDark: #412
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
This just starts as braindump, I will refine it soon:
-* handle all files Lumiera uses at runtime (media, edl, temp data)
-* manage filehandles, Lumiera might use more more files than available filehandles
-* manage temporary data
-* do caching
-* io will be blocked where the backend tells the core where it can expect the data (not read()/write() like)
-* kind-of garbage collector
-* do prefetching
-* no/low latency for the core the prefetcher and other things ensure that data is available in time
-* translate any input into a format which the Lumiera core understands (demux, decode)
-* same for encoding to output formats
-* offer a plugin API for encoders/decoders
-* maybe network backend for serving data to distributed render nodes
-* can do some load control or management (trigger adaptive rendering if system is idle etc)
-* pull based arch
-* Serialize persistent data (Project / EDL's)
-
-Look at [[Overview]] for the current design proposal
-
-
-
-
DataBackend
-
-
-
-
[[File]] associates a filename with the underlying FileDescriptor. This allows [[File]]s which have serveral names (hardlinks) to share a underlying backend.
-
-
-
'FileDescriptor' is the superclass of all possible filetypes, it has a weak reference to a FileHandle which is managed in FilehandleCache, on creation only the existence (when reading) or access for write for new files are checked. 'FileDescriptor' stores some generic metadata about the underlying file and intended use. But actual opening is done on demand.
-
-The content is memory mapped into the process address space, this is managed by FileMap objects and a FileMapCache.
-
-
-
-
'FileHandle's are managed by the FileHandleCache, they are just storing the underlying OS file handles and managed in a lazy/weak way, (re)opened when needed and aging in the cache when not needed, since the amount of open file handles is limited aged ones will be closed and reused when the system needs to open another file.
-
-
-
-
Each 'FileMap' object contains many [[Frame]]s. The actual layout depends on the type of the [[File]]. Mappings need to be page aligned while [[Frame]]s can be anywhere within a file and dynamically sized.
-
-All established [[FileMap]]s are managed in a FileMapCache. This is similar to the FileHandleCache, but mappings which are in use are checked out of the aging list and thus become locked from aging/purging.
-
-FileMap objects are transparent to the application. It will only requests [[Frame]]s as in position and size (and some other parameters).
-
-
-
-
The 'FileMapCache' keeps a list of FileMaps which are currently not in use and subject of aging.
-Whenever a FileMap is in use, it is checked out into an in-use list where it is not subject to aging.
-
-
-
-
'FilehandleCache' storing a finite maximum number of [[FileHandle]]s as a list. As long the configured maximum of open files is not reached new file handles are stored at the begin of the list. Whenever a filehandle is accessed it is moved to the begin of the list too. Unused filehandles propagate towards the end of the list. When the maximum of open filehandles is reached, aged filehandles are closed and taken from the end.
-
-
-
-
'Frames' are the smallest datablocks handled by the Backend. The application tells the Backend to make [[File]]s available and then only requests Frames from the Backend. All other datastructures of the backend are private.
-
-Actually Frames are (references to) blocks of continuous memory. They can be anything depending on the usage of the [[File]] (Video frames, encoder frames, blocks of sound samples).
-
-Each [[Frame]] points to a [[FrameDescriptor]] which describes the shared properties of [[Frame]]s of the same kind. For video frames this [[FrameDescriptor]] will define the policies of the used color model, resolution, aspect ratio and so on, for example.
-
-Frames are referenced by a smart-pointer like object which manages the lifetime and caching behavior. There are 3 states such a frame reference can be in:
-# readonly: the backing FileMap is checked out from the aging list, frames can be read
-# readwrite: the backing FileMap is checked out from the aging list, frames can be read and written (depends on the filemode as well)
-# weak: the FileMap object is checked back into the aging list, the frame can't be accessed but we can try to transform a weak reference into a readonly or readwrite reference
-
-Frames can be addressed uniquely (needs to be worked out) whenever a frame is not available. The backend can initiate a (probably recursive) render for it.
-
-Accessing [[Frame]]s may add further renderjobs for related frames to the [[Prefetch]] task.
-
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
-
/***
-|''Name:''|InlineJavascriptPlugin|
-|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
-|''Author:''|Eric Shulman - ELS Design Studios|
-|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
-|''~CoreVersion:''|2.0.10|
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Display script source in tiddler output''
-By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-><script show>
- alert('InlineJavascriptPlugin: this is a demonstration message');
-</script>
-dynamic output:
-><script show>
- return (new Date()).toString();
-</script>
-wikified dynamic output:
-><script show>
- return "link to current user: [["+config.options.txtUserName+"]]";
-</script>
-dynamic output using 'place' to get size information for current tiddler:
-><script show>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-creating an 'onclick' button/link that runs a script:
-><script label="click here" show>
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-loading a script from a source url:
->http://www.TiddlyTools.com/demo.js contains:
->>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
->>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
-><script src="demo.js" show>
- return "loading demo.js..."
-</script>
-><script label="click to execute demo() function" show>
- demo()
-</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
-''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
-''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]'' initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[4]) { // there is script code
- if (lookaheadMatch[3]) // show inline script code in tiddler output
- wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
- if (lookaheadMatch[2]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[4]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else { // run inline script code
- var code="function _out(place){"+lookaheadMatch[4]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
- }
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
''[[Lumiera|index.html]]''
-DataBackend
-[[Overview]]
-<<fullscreen>>
-
-
-
-
<!--{{{-->
-<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
-<!--}}}-->
-
-<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>My TiddlyWiki</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>
-
-
-
how is FileMetadata kept
-
-copying semantics of smart pointers
-
-explain opening/closing files (use() forget()?)
-
-difference between actual files and temporary. does it make sense to have temporary storage on diffent speed disks?
-
-statistics hit/fail max/min/avg timings, hard / soft fails, timing constraints, when is rerendering cheaper than caching?..
-
-adaptive rendering
-
-background rendering
-
-renderfarm
-
-[[FrameDescriptor]]s and [[Frame]] details, Policies composing frames
-
-Storage and logging of EDL's, unlimited undo, database,...
-
-When to Cache and when not to cache, aka instant [[Frame]] reuse
-
-
-
-
Whenever Lumiera needs to access data this is done through the DataBackend described here. 
-
-There are two main kinds how data is handled:
-* Project Description and EDL's are handled in a InMemoryDatabase which uses a [[Serializer]] for storing and logging modifications.
-* Media (audio, video, ...) is mapped as described below.
-
-The backend uses memory mapping to make data available to the program. This is little different to more common open/read/write/close file access while giving superior performance and much better memory utilization.
-
-The data backend must be capable to handle more data than will fit into the memory or even address space on 32 bit architectures.  Moreover a project may access more files than the OS can handle at a time, thus the for [[File]]s used by the Backend it needs a FilehandleCache to manage filehandles dynamically.
-
-Which parts of a File are actually mapped to physical RAM is managed by the kernel, it keeps a FileMapCache to manage the [[FileMap]]s we've set up.
-
-The application itself only requests [[Frame]]s from the backend.
-
-To minimize latency and optimize CPU utilization we have a [[Prefetch]] thread which operates a [[Scheduler]] to render and cache frames which are expected to be consumed soon. This prefetcher keeps [[Statistics]] for optimizing performance.
-
-
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-	<div class='headerShadow'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-	<div class='headerForeground'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-</div>
-<!-- horizontal MainMenu -->
-<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
-<!-- original MainMenu menu -->
-<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
-<div id='sidebar'>
-	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-	<div id='messageArea'></div>
-	<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
-
/***
-|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
-|''Version:''|1.0.6 (2006-11-07)|
-|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
-|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
-|''Licence:''|[[BSD open source license]]|
-|''TiddlyWiki:''|2.0|
-|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
-!Table of Content<html><a name="TOC"/></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
-!Description<html><a name="Description"/></html>
-With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
-Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts, use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
-
-''Syntax:'' 
-|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
-|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//.|
-|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
-|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
-|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Applications<html><a name="Applications"/></html>
-!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
-Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
-
-Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Citation Index<html><a name="Citation"/></html>
-Create a tiddler "Citations" that contains your "citations". 
-Wrap every citation with a part and a proper name. 
-
-''Example''
-{{{
-<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
-in //Proc. ICSM//, 1998.</part>
-
-<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
-Thesis, Uni Stuttgart, 2002.</part>
-
-<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
-in //Proc. ICSM//, 1999.</part>
-}}}
-
-You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
-You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
-{{{
-* Item 1
-* Item 2
-* Item 3
-}}}
-into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
-
-Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
-
-''Example''
-{{{
-|!Subject|!Items|
-|subject1|<<tiddler ./Cell1>>|
-|subject2|<<tiddler ./Cell2>>|
-
-<part Cell1 hidden>
-* Item 1
-* Item 2
-* Item 3
-</part>
-...
-}}}
-
-Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
-
-BTW: The same approach can be used to create bullet lists with items that contain more than one line.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating Tabs<html><a name="Tabs"/></html>
-The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
-
-With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
-
-''Example''
-The standard tabs at the sidebar are defined by the following eight tiddlers:
-* SideBarTabs
-* TabAll
-* TabMore
-* TabMoreMissing
-* TabMoreOrphans
-* TabMoreShadowed
-* TabTags
-* TabTimeline
-
-Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
-{{{
-<<tabs txtMainTab 
- Timeline Timeline SideBarTabs/Timeline 
- All 'All tiddlers' SideBarTabs/All 
- Tags 'All tags' SideBarTabs/Tags 
- More 'More lists' SideBarTabs/More>>
-<part Timeline hidden><<timeline>></part>
-<part All hidden><<list all>></part>
-<part Tags hidden><<allTags>></part>
-<part More hidden><<tabs txtMoreTab 
- Missing 'Missing tiddlers' SideBarTabs/Missing 
- Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
- Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
-<part Missing hidden><<list missing>></part>
-<part Orphans hidden><<list orphans>></part>
-<part Shadowed hidden><<list shadowed>></part>
-}}}
-
-Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
-
-E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
-{{{
-<<forEachTiddler 
- sortBy 'tiddler.modified' descending 
- write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
-}}}
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Using Sliders<html><a name="Sliders"/></html>
-Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
-
-''Example''
-In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
-{{{
-...
-<<slider chkAboutDetails About/Details details "Click here to see more details">>
-<part Details hidden>
-To give you a better overview ...
-</part>
-...
-}}}
-
-Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Revision history<html><a name="Revisions"/></html>
-* v1.0.6 (2006-11-07)
-** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
-* v1.0.5 (2006-03-02)
-** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
-* v1.0.4 (2006-02-28)
-** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
-* v1.0.3 (2006-02-26)
-** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
-* v1.0.2 (2006-02-05)
-** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
-* v1.0.1 (2006-01-27)
-** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
-** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
-* v1.0.0 (2006-01-25)
-** initial version
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Code<html><a name="Code"/></html>
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-//{{{
-//============================================================================
-// PartTiddlerPlugin
-
-// Ensure that the PartTiddler Plugin is only installed once.
-//
-if (!version.extensions.PartTiddlerPlugin) {
-
-
-
-version.extensions.PartTiddlerPlugin = {
- major: 1, minor: 0, revision: 6,
- date: new Date(2006, 10, 7), 
- type: 'plugin',
- source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
-};
-
-if (!window.abego) window.abego = {};
-if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
-
-//============================================================================
-// Common Helpers
-
-// Looks for the next newline, starting at the index-th char of text. 
-//
-// If there are only whitespaces between index and the newline 
-// the index behind the newline is returned, 
-// otherwise (or when no newline is found) index is returned.
-//
-var skipEmptyEndOfLine = function(text, index) {
- var re = /(\n|[^\s])/g;
- re.lastIndex = index;
- var result = re.exec(text);
- return (result && text.charAt(result.index) == '\n') 
- ? result.index+1
- : index;
-}
-
-
-//============================================================================
-// Constants
-
-var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
-var partEndTagREString = "<\\/part>";
-var partEndTagString = "</part>";
-
-//============================================================================
-// Plugin Specific Helpers
-
-// Parse the parameters inside a <part ...> tag and return the result.
-//
-// @return [may be null] {partName: ..., isHidden: ...}
-//
-var parseStartTagParams = function(paramText) {
- var params = paramText.readMacroParams();
- if (params.length == 0 || params[0].length == 0) return null;
- 
- var name = params[0];
- var paramsIndex = 1;
- var hidden = false;
- if (paramsIndex < params.length) {
- hidden = params[paramsIndex] == "hidden";
- paramsIndex++;
- }
- 
- return {
- partName: name, 
- isHidden: hidden
- };
-}
-
-// Returns the match to the next (end or start) part tag in the text, 
-// starting the search at startIndex.
-// 
-// When no such tag is found null is returned, otherwise a "Match" is returned:
-// [0]: full match
-// [1]: matched "end" tag (or null when no end tag match)
-// [2]: matched "start" tag (or null when no start tag match)
-// [3]: content of start tag (or null if no start tag match)
-//
-var findNextPartEndOrStartTagMatch = function(text, startIndex) {
- var re = new RegExp(partEndOrStartTagRE);
- re.lastIndex = startIndex;
- var match = re.exec(text);
- return match;
-}
-
-//============================================================================
-// Formatter
-
-// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
-//
-// @return true if a complete part section (including the end tag) could be processed, false otherwise.
-//
-var handlePartSection = function(w) {
- var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
- if (!tagMatch) return false;
- if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
-
- // Parse the start tag parameters
- var arguments = parseStartTagParams(tagMatch[3]);
- if (!arguments) return false;
- 
- // Continue processing
- var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
- var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
- if (endMatch && endMatch[1]) {
- if (!arguments.isHidden) {
- w.nextMatch = startTagEndIndex;
- w.subWikify(w.output,partEndTagREString);
- }
- w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
- 
- return true;
- }
- return false;
-}
-
-config.formatters.push( {
- name: "part",
- match: "<part\\s+[^>]+>",
- 
- handler: function(w) {
- if (!handlePartSection(w)) {
- w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
- }
- }
-} )
-
-//============================================================================
-// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
-// as tiddlers.
-
-var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
-
-// Return the match to the first <part ...> tag of the text that has the
-// requrest partName.
-//
-// @return [may be null]
-//
-var findPartStartTagByName = function(text, partName) {
- var i = 0;
- 
- while (true) {
- var tagMatch = findNextPartEndOrStartTagMatch(text, i);
- if (!tagMatch) return null;
-
- if (tagMatch[2]) {
- // Is start tag
- 
- // Check the name
- var arguments = parseStartTagParams(tagMatch[3]);
- if (arguments && arguments.partName == partName) {
- return tagMatch;
- }
- }
- i += tagMatch[0].length;
- }
-}
-
-// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
-// object, using fullName as the Tiddler's title. 
-//
-// All remaining properties of the new Tiddler (tags etc.) are inherited from 
-// the parentTiddler.
-// 
-// @return [may be null]
-//
-var getPart = function(parentTiddler, partName, fullName) {
- var text = parentTiddler.text;
- var startTag = findPartStartTagByName(text, partName);
- if (!startTag) return null;
- 
- var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
- var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
-
- if (indexOfEndTag >= 0) {
- var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
- var partTiddler = new Tiddler();
- partTiddler.set(
- fullName,
- partTiddlerText,
- parentTiddler.modifier,
- parentTiddler.modified,
- parentTiddler.tags,
- parentTiddler.created);
- partTiddler.abegoIsPartTiddler = true;
- return partTiddler;
- }
- 
- return null;
-}
-
-// Hijack the store.fetchTiddler to recognize the "part" addresses.
-//
-
-var oldFetchTiddler = store.fetchTiddler ;
-store.fetchTiddler = function(title) {
- var result = oldFetchTiddler.apply(this, arguments);
- if (!result && title) {
- var i = title.lastIndexOf('/');
- if (i > 0) {
- var parentName = title.substring(0, i);
- var partName = title.substring(i+1);
- var parent = (parentName == ".") 
- ? currentParent 
- : oldFetchTiddler.apply(this, [parentName]);
- if (parent) {
- return getPart(parent, partName, parent.title+"/"+partName);
- }
- }
- }
- return result; 
-};
-
-
-// The user must not edit a readOnly/partTiddler
-//
-
-config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
-
-Tiddler.prototype.isReadOnly = function() {
- // Tiddler.isReadOnly was introduced with TW 2.0.6.
- // For older version we explicitly check the global readOnly flag
- if (config.commands.editTiddler.oldIsReadOnlyFunction) {
- if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
- } else {
- if (readOnly) return true;
- }
-
- return this.abegoIsPartTiddler;
-}
-
-config.commands.editTiddler.handler = function(event,src,title)
-{
- var t = store.getTiddler(title);
- // Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
- // or the tiddler is not readOnly
- if(!t || !t.abegoIsPartTiddler)
- {
- clearMessage();
- story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
- story.focusTiddler(title,"text");
- return false;
- }
-}
-
-// To allow the "./partName" syntax in macros we need to hijack 
-// the invokeMacro to define the "currentParent" while it is running.
-// 
-var oldInvokeMacro = window.invokeMacro;
-function myInvokeMacro(place,macro,params,wikifier,tiddler) {
- var oldCurrentParent = currentParent;
- if (tiddler) currentParent = tiddler;
- try {
- oldInvokeMacro.apply(this, arguments);
- } finally {
- currentParent = oldCurrentParent;
- }
-}
-window.invokeMacro = myInvokeMacro;
-
-// Scroll the anchor anchorName in the viewer of the given tiddler visible.
-// When no tiddler is defined use the tiddler of the target given event is used.
-window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
- var tiddlerElem = null;
- if (tiddler) {
- tiddlerElem = document.getElementById(story.idPrefix + tiddler);
- }
- if (!tiddlerElem && evt) {
- var target = resolveTarget(evt);
- tiddlerElem = story.findContainingTiddler(target);
- }
- if (!tiddlerElem) return;
-
- var children = tiddlerElem.getElementsByTagName("a");
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var name = child.getAttribute("name");
- if (name == anchorName) {
- var y = findPosY(child);
- window.scrollTo(0,y);
- return;
- }
- }
-}
-
-} // of "install only once"
-//}}}
-
-/***
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Licence and Copyright
-Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-Neither the name of abego Software nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-
-
-
There are 2 important points when we want to access data with low latency:
-# Since we handle much more data than it will fit into most computers RAM. The data which is backed in files has to be paged in and available when needed. The [[Prefetch]] Thread manages page hinting to the kernel (posix_madvise()..)
-# Intermediate [[Frame]]s must eventually be rendered to the cache. The Backend will send Renderjobs to the Controller.
-
-Both of these actions are managed by a [[Scheduler]].
-
-Whenever something queries a [[Frame]] from the backend it provides hints about what it is doing.
-These hints contain:
-
-* Timing constraints
-** When will the [[Frame]] be needed
-** could we drop the request if it won't be available (rendered) in-time
-* Priority of this job (as soon as possible, or just in time?)
-* action (Playing forward, playing backward, tweaking, playback speed, recursive rendering of dependent frames)
-
-Notes:
-* The Backend will try to render related frames in groups.
-** This means that following frames are scheduled with lower priority. Whenever the program really requests them the priority will be adjusted.
-
-
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
//last update: RSSReaderPlugin v 1.1.1//
-
-!Description
-This plugin provides a RSSReader for TiddlyWiki
-* It accesses asynchronously an RSSFeed
-*Depending on the chanel item format, each item could be written as :
-**simple text wikified
-**html
-
-!Usage
-{{{
-<<rssReader noDesc|asHtml|asText rssUrl ['filtering string']>>
-	noDesc: only title of item is printed
-
-	asHtml: if you know that description contain html (links, img ...), 
-		the text is enclosed with <html> </html> tags
-
- 	asText: if the description should not be interpreted as html the 
-		description is wikified
-
-	rssUrl: the rssFeed url that could be accessed. 
-	
-	'filtering string': if present, the rssfeed item title must contained 
-		this string to be displayed. 
-		If 'filering string' contained space characters only, the tiddler 
-		title is used for filtering.
-
-}}}
-
-For security reasons, if the TiddlyWiki is accessed from http, a ProxyService should be used to access an rssFeed from an other site.
-
-!examples
-| !reader | !RSSFeed type | !working from |
-| BidiXTWRSS | Description asHtml | file: or tiddlywiki.bidix.info |
-| [[Le Monde]] | Description asText | file: or tiddlywiki.bidix.info using proxy |
-| YahooNewsSport | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| TiddlyWikiRSS | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| [[Libération]] | noDesc | file: or tiddlywiki.bidix.info using proxy |
-| [[TestComment]] | asText and filters | file: or tiddlywiki.bidix.info using proxy |
-see : <<tag RSSFeed>> for the full list.
-
-!Revision history
-* V1.1.0 (2207/04/13)
-**No more import functions
-* V1.0.0 (2006/11/11)
-**refactoring using core loadRemoteFile function
-**import using new tiddlywiki:tiddler element
-**import and presentation preserved without EricShulman's NestedSliderPlugin
-**better display of items 
-* v0.3.0 (24/08/2006)
-** Filter on RSS item title
-** Place to display redefined for asynchronous processing
-* v0.2.2 (22/08/2006)
-**Haloscan feed has no pubDate.
-* v0.2.1 (08/05/2006)
-* v0.2.0 (01/05/2006)
-**Small adapations for del.icio.us feed
-* v0.1.1 (28/04/2006)
-**Bug : Channel without title 
-* v0.1.0 (24/04/2006)
-** initial release
-
-
-
-
-
-
Scheduling is done with two priority queues, one for high priority jobs and one for low priority jobs. These priority queues are ordered by absolute time values (and a job identifier, details will be worked out at implementation time).
-
-there are following (non exhaustive) kinds of jobs:
-* start job
-* cancel job, if not finished (abort when out of time)
-* unschedule job
-
-Jobs implement a kind of future, datastructures which block a querier until data is available.
-
-The Job scheduler runs singlethreaded. Its only task is to schedule and delegate jobs to worker threads, by itself it will never do any extensive processing!
-
-Each job has an option what to do when its times expires (abort, proceed).
-
-The high priority queue is ordered by the __start__ time T, when the job has to be started (plus some hystersis H). A high priority job becomes scheduled beginning with time T but no later than T+H. 
-
-The low priority queue is ordered by __end__ time T, when the job has to be finished (minus a spawn S). Low priority jobs are started as soon as system load permits, the high priority queue is empty (for some usec in future, lets say 100) and the time is earlier than ~T-S. When a job is expired, it is removed from the queue, when it is already running it is handled as defined in its expire policy.
-
-Canceling and expireing jobs gets noted in Statistics to adjust performance and timings for optimal performance.
-
-
-
-
<<search>><<closeAll>><<permaview>><<newTiddler>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
-
-
-
design draft
-
-
-
-
Data Backend
-
-
-
/***
-
-''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
-
-|Name|SplashScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#SplashScreenPlugin|
-|Version|0.21 |
-|Requires|~TW2.08+|
-!Description:
-Provides a simple splash screen that is visible while the TW is loading.
-
-!Installation
-Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
-
-!Customizing
-Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
-
-!History
-* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
-* 26-06-06 : version 0.2, first release
-
-!Code
-***/
-//{{{
-var old_lewcid_splash_restart=restart;
-
-restart = function()
-{   if (document.getElementById("SplashScreen"))
-        document.getElementById("SplashScreen").style.display = "none";
-      if (document.getElementById("contentWrapper"))
-        document.getElementById("contentWrapper").style.display = "block";
-    
-    old_lewcid_splash_restart();
-   
-    if (splashScreenInstall)
-       {if(config.options.chkAutoSave)
-			{saveChanges();}
-        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
-        }
-}
-
-
-var oldText = store.getTiddlerText("MarkupPreHead");
-if (oldText.indexOf("SplashScreen")==-1)
-   {var siteTitle = store.getTiddlerText("SiteTitle");
-   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
-   if (! store.tiddlerExists("MarkupPreHead"))
-       {var myTiddler = store.createTiddler("MarkupPreHead");}
-   else
-      {var myTiddler = store.getTiddler("MarkupPreHead");}
-      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
-      store.setDirty(true);
-      var splashScreenInstall = true;
-}
-//}}}
-
-
-
/*{{{*/
-/* a contrasting background so I can see where one tiddler ends and the other begins */
-body {
-	background: [[ColorPalette::TertiaryLight]];
-}
-
-/* sexy colours and font for the header */
-.headerForeground {
-	color: [[ColorPalette::PrimaryPale]];
-}
-.headerShadow, .headerShadow a {
-	color: [[ColorPalette::PrimaryMid]];
-}
-.headerForeground, .headerShadow {
-	padding: 1em 1em 0;
-	font-family: 'Trebuchet MS' sans-serif;
-	font-weight:bold;
-}
-.headerForeground .siteSubtitle {
-	color: [[ColorPalette::PrimaryLight]];
-}
-.headerShadow .siteSubtitle {
-	color: [[ColorPalette::PrimaryMid]];
-}
-
-/* make shadow go and down right instead of up and left */
-.headerShadow {
-	left: 2px;
-	top: 3px;
-}
-
-/* prefer monospace for editing */
-.editor textarea {
-	font-family: 'Consolas' monospace;
-}
-
-/* sexy tiddler titles */
-.title {
-	font-size: 250%;
-	color: [[ColorPalette::PrimaryLight]];
-	font-family: 'Trebuchet MS' sans-serif;
-}
-
-/* more subtle tiddler subtitle */
-.subtitle {
-	padding:0px;
-	margin:0px;
-	padding-left:0.5em;
-	font-size: 90%;
-	color: [[ColorPalette::TertiaryMid]];
-}
-.subtitle .tiddlyLink {
-	color: [[ColorPalette::TertiaryMid]];
-}
-
-/* a little bit of extra whitespace */
-.viewer {
-	padding-bottom:3px;
-}
-
-/* don't want any background color for headings */
-h1,h2,h3,h4,h5,h6 {
-	background: [[ColorPalette::Background]];
-	color: [[ColorPalette::Foreground]];
-}
-
-/* give tiddlers 3d style border and explicit background */
-.tiddler {
-	background: [[ColorPalette::Background]];
-	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
-	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
-	margin-bottom: 1em;
-	padding-bottom: 2em;
-}
-
-/* make options slider look nicer */
-#sidebarOptions .sliderPanel {
-	border:solid 1px [[ColorPalette::PrimaryLight]];
-}
-
-
-/* the borders look wrong with the body background */
-#sidebar .button {
-	border-style: none;
-}
-
-/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
-.tagglyTagged li.listTitle {
-	display:none
-}
-.tagglyTagged li {
-	display: inline; font-size:90%;
-}
-.tagglyTagged ul {
-	margin:0px; padding:0px;
-}
-
-/* this means you can put line breaks in SidebarOptions for readability */
-#sidebarOptions br {
-	display:none;
-}
-/* undo the above in OptionsPanel */
-#sidebarOptions .sliderPanel br {
-	display:inline;
-}
-
-/* horizontal main menu stuff */
-#displayArea {
-	margin: 1em 15.7em 0em 1em; /* use the freed up space */
-}
-#topMenu br {
-	display: none;
-}
-#topMenu {
-	background: [[ColorPalette::PrimaryMid]];
-	color:[[ColorPalette::PrimaryPale]];
-}
-#topMenu {
-	padding:2px;
-}
-#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
-	margin-left: 0.5em;
-	margin-right: 0.5em;
-	padding-left: 3px;
-	padding-right: 3px;
-	color: [[ColorPalette::PrimaryPale]];
-	font-size: 115%;
-}
-#topMenu .button:hover, #topMenu .tiddlyLink:hover {
-	background: [[ColorPalette::PrimaryDark]];
-}
-
-/* make it print a little cleaner */
-@media print {
-	#topMenu {
-		display: none ! important;
-	}
-	/* not sure if we need all the importants */
-	.tiddler {
-		border-style: none ! important;
-		margin:0px ! important;
-		padding:0px ! important;
-		padding-bottom:2em ! important;
-	}
-	.tagglyTagging .button, .tagglyTagging .hidebutton {
-		display: none ! important;
-	}
-	.headerShadow {
-		visibility: hidden ! important;
-	}
-	.tagglyTagged .quickopentag, .tagged .quickopentag {
-		border-style: none ! important;
-	}
-	.quickopentag a.button, .miniTag {
-		display: none ! important;
-	}
-}
-/*}}}*/
-
-
-
-
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
-
- - - - - - - - - - diff --git a/wiki/compatibility.html b/wiki/compatibility.html deleted file mode 100644 index 13a2392a4..000000000 --- a/wiki/compatibility.html +++ /dev/null @@ -1,11655 +0,0 @@ - - - - - - - - - - - -
My TiddlyWiki is loading ...

Requires Javascript.
- - Compatibility - Dependencies and Style - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #8cf
-PrimaryLight: #18f
-PrimaryMid: #04b
-PrimaryDark: #014
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
/*{{{*/
-body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-
-a {color:[[ColorPalette::PrimaryMid]];}
-a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
-a img {border:0;}
-
-h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
-h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
-h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
-
-.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
-.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
-.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
-
-.header {background:[[ColorPalette::PrimaryMid]];}
-.headerShadow {color:[[ColorPalette::Foreground]];}
-.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
-.headerForeground {color:[[ColorPalette::Background]];}
-.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
-
-.tabSelected{color:[[ColorPalette::PrimaryDark]];
-	background:[[ColorPalette::TertiaryPale]];
-	border-left:1px solid [[ColorPalette::TertiaryLight]];
-	border-top:1px solid [[ColorPalette::TertiaryLight]];
-	border-right:1px solid [[ColorPalette::TertiaryLight]];
-}
-.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
-.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
-.tabContents .button {border:0;}
-
-#sidebar {}
-#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
-#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
-
-.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
-.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
-.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
-	border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
-.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
-.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
-.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
-	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
-.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
-.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
-	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
-
-#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
-#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
-
-.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
-
-.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
-.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
-.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
-.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
-
-.tiddler .defaultCommand {font-weight:bold;}
-
-.shadow .title {color:[[ColorPalette::TertiaryDark]];}
-
-.title {color:[[ColorPalette::SecondaryDark]];}
-.subtitle {color:[[ColorPalette::TertiaryDark]];}
-
-.toolbar {color:[[ColorPalette::PrimaryMid]];}
-.toolbar a {color:[[ColorPalette::TertiaryLight]];}
-.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
-.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
-
-.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
-.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
-.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
-.tagging .button, .tagged .button {border:none;}
-
-.footer {color:[[ColorPalette::TertiaryLight]];}
-.selected .footer {color:[[ColorPalette::TertiaryMid]];}
-
-.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
-.sparktick {background:[[ColorPalette::PrimaryDark]];}
-
-.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
-.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
-.lowlight {background:[[ColorPalette::TertiaryLight]];}
-
-.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
-
-.imageLink, #displayArea .imageLink {background:transparent;}
-
-.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
-
-.viewer .listTitle {list-style-type:none; margin-left:-2em;}
-.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
-.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
-.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
-.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
-.viewer code {color:[[ColorPalette::SecondaryDark]];}
-.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
-
-.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
-
-.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
-.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
-.editorFooter {color:[[ColorPalette::TertiaryMid]];}
-
-#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
-#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
-#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
-#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
-.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
-.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
-#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
-/*}}}*/
-
-
-
/*{{{*/
-* html .tiddler {height:1%;}
-
-body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
-
-h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
-h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
-h4,h5,h6 {margin-top:1em;}
-h1 {font-size:1.35em;}
-h2 {font-size:1.25em;}
-h3 {font-size:1.1em;}
-h4 {font-size:1em;}
-h5 {font-size:.9em;}
-
-hr {height:1px;}
-
-a {text-decoration:none;}
-
-dt {font-weight:bold;}
-
-ol {list-style-type:decimal;}
-ol ol {list-style-type:lower-alpha;}
-ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol {list-style-type:decimal;}
-ol ol ol ol ol {list-style-type:lower-alpha;}
-ol ol ol ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol ol ol ol {list-style-type:decimal;}
-
-.txtOptionInput {width:11em;}
-
-#contentWrapper .chkOptionInput {border:0;}
-
-.externalLink {text-decoration:underline;}
-
-.indent {margin-left:3em;}
-.outdent {margin-left:3em; text-indent:-3em;}
-code.escaped {white-space:nowrap;}
-
-.tiddlyLinkExisting {font-weight:bold;}
-.tiddlyLinkNonExisting {font-style:italic;}
-
-/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
-a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
-
-#mainMenu .tiddlyLinkExisting,
-	#mainMenu .tiddlyLinkNonExisting,
-	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
-#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
-
-.header {position:relative;}
-.header a:hover {background:transparent;}
-.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
-.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
-
-.siteTitle {font-size:3em;}
-.siteSubtitle {font-size:1.2em;}
-
-#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
-
-#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
-#sidebarOptions {padding-top:0.3em;}
-#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
-#sidebarOptions input {margin:0.4em 0.5em;}
-#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
-#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
-#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
-#sidebarTabs .tabContents {width:15em; overflow:hidden;}
-
-.wizard {padding:0.1em 1em 0em 2em;}
-.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizardStep {padding:1em 1em 1em 1em;}
-.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
-.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
-.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
-.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
-
-#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
-.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
-#messageArea a {text-decoration:underline;}
-
-.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
-.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
-
-.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
-.popup .popupMessage {padding:0.4em;}
-.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
-.popup li.disabled {padding:0.4em;}
-.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
-.listBreak {font-size:1px; line-height:1px;}
-.listBreak div {margin:2px 0;}
-
-.tabset {padding:1em 0em 0em 0.5em;}
-.tab {margin:0em 0em 0em 0.25em; padding:2px;}
-.tabContents {padding:0.5em;}
-.tabContents ul, .tabContents ol {margin:0; padding:0;}
-.txtMainTab .tabContents li {list-style:none;}
-.tabContents li.listLink { margin-left:.75em;}
-
-#contentWrapper {display:block;}
-#splashScreen {display:none;}
-
-#displayArea {margin:1em 17em 0em 14em;}
-
-.toolbar {text-align:right; font-size:.9em;}
-
-.tiddler {padding:1em 1em 0em 1em;}
-
-.missing .viewer,.missing .title {font-style:italic;}
-
-.title {font-size:1.6em; font-weight:bold;}
-
-.missing .subtitle {display:none;}
-.subtitle {font-size:1.1em;}
-
-.tiddler .button {padding:0.2em 0.4em;}
-
-.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
-.isTag .tagging {display:block;}
-.tagged {margin:0.5em; float:right;}
-.tagging, .tagged {font-size:0.9em; padding:0.25em;}
-.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
-.tagClear {clear:both;}
-
-.footer {font-size:.9em;}
-.footer li {display:inline;}
-
-.annotation {padding:0.5em; margin:0.5em;}
-
-* html .viewer pre {width:99%; padding:0 0 1em 0;}
-.viewer {line-height:1.4em; padding-top:0.5em;}
-.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
-.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
-.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
-
-.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
-.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
-table.listView {font-size:0.85em; margin:0.8em 1.0em;}
-table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
-
-.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
-.viewer code {font-size:1.2em; line-height:1.4em;}
-
-.editor {font-size:1.1em;}
-.editor input, .editor textarea {display:block; width:100%; font:inherit;}
-.editorFooter {padding:0.25em 0em; font-size:.9em;}
-.editorFooter .button {padding-top:0px; padding-bottom:0px;}
-
-.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
-
-.sparkline {line-height:1em;}
-.sparktick {outline:0;}
-
-.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
-.zoomer div {padding:1em;}
-
-* html #backstage {width:99%;}
-* html #backstageArea {width:99%;}
-#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageToolbar {position:relative;}
-#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
-#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
-#backstage {position:relative; width:100%; z-index:50;}
-#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
-.backstagePanelFooter {padding-top:0.2em; float:right;}
-.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
-#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
-
-.whenBackstage {display:none;}
-.backstageVisible .whenBackstage {display:block;}
-/*}}}*/
-
-
-
/***
-StyleSheet for use when a translation requires any css style changes.
-This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
-***/
-
-/*{{{*/
-body {font-size:0.8em;}
-
-#sidebarOptions {font-size:1.05em;}
-#sidebarOptions a {font-style:normal;}
-#sidebarOptions .sliderPanel {font-size:0.95em;}
-
-.subtitle {font-size:0.8em;}
-
-.viewer table.listView {font-size:0.95em;}
-
-.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
-/*}}}*/
-
-
-
/*{{{*/
-@media print {
-#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
-#displayArea {margin: 1em 1em 0em 1em;}
-/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
-noscript {display:none;}
-}
-/*}}}*/
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-<div class='headerShadow'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-<div class='headerForeground'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-</div>
-<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
-<div id='sidebar'>
-<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-<div id='messageArea'></div>
-<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
-<div class='title' macro='view title'></div>
-<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
-<div class='tagging' macro='tagging'></div>
-<div class='tagged' macro='tags'></div>
-<div class='viewer' macro='view text wikified'></div>
-<div class='tagClear'></div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
-<div class='title' macro='view title'></div>
-<div class='editor' macro='edit title'></div>
-<div macro='annotations'></div>
-<div class='editor' macro='edit text'></div>
-<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
-<!--}}}-->
-
-
-
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
-* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
-* MainMenu: The menu (usually on the left)
-* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
-You'll also need to enter your username for signing your edits: <<option txtUserName>>
-
-
-
These InterfaceOptions for customising TiddlyWiki are saved in your browser
-
-Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
-
-<<option txtUserName>>
-<<option chkSaveBackups>> SaveBackups
-<<option chkAutoSave>> AutoSave
-<<option chkRegExpSearch>> RegExpSearch
-<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
-<<option chkAnimate>> EnableAnimations
-
-----
-Also see AdvancedOptions
-
-
- -
-
-
A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far.  Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:
-<<<
-<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.
-
-Try modifying the time spent.  Suppose you've just spent one more hour and want to record it.  Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window.  Watch the time remaining go down to 1 hour.
-
-In reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours.  The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:
-<<<
-<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-You can adjust the current estimate in the same way as you adjusted the time spent.  Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".
-
-You can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases).  Click on the time remaining and add an hour by typing "+1".
-
-When the time remaining goes to zero, the task is considered complete:
-<<<
-<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you haven't already done so, try double-clicking the description.  Yes, it really does open up the editor and select just the text of the description.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 0.5>> Put tasksum on the ViewTemplate.
-<<<
-You can specify just the description of a task, and leave it unestimated.  Click the question mark to enter the estimate:
-<<<
-<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-As this task implies, you can enter two values in the popup when you click on any of the time cells.  Separate them with spaces and/or a comma.  Experiment:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-Finally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there.  That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.
-
-And setting up a taskadder is simplicity itself.  Just add {{{<<taskadder>>}}} to your tiddler.  You will see this:
-<<<
-<<taskadder>>
-<<<
-Just type a task description into the first field, and your initial estimate for how long it will take into the second field.  Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler.  Notice that you can just start typing a new task as soon as you're done entering the first one.
-
-You can have as many taskadders as you like in any tiddler.  The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse.  Try adding some tasks here and in the above adder:
-<<<
-<<taskadder>>
-<<<
-Notice that the one you just used takes focus when this tiddler is redisplayed.
-
-A taskadder by default adds tasks above itself.  You can make it add them below by adding a {{{below}}} argument to the macro call:
-<<<
-<<taskadder below>>
-<<<
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
In this tutorial, we've been looking mostly at individual tasks.  In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler.  In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.
-
-To get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them.  Here's an example:
-<<<
-<<tasksum start>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end>>
-<<<
-If you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.
-<<<
-<<tasksum start here>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<tasksum end>>
-<<<
-You can nest these things if you like, just be sure to match starts and ends:
-<<<
-<<tasksum start here>>
-* Time cell manipulation:<<tasksum start>>
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<tasksum end "Cell manipulation:">>
-<<br>>
-* Double-click handling:<<tasksum start>>
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end "Double-clicks:">>
-
-<<tasksum end>>
-<<<
-Finally, the simplest way to use tasksum is to add it to your view template.  See TaskSummaryViewTemplate for an example template.  Note that if no tasks are present between the start and end, nothing is displayed.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort.  However, there are two issues that may affect you.  (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)
-# The task macros don't play nicely with the default TiddlyWiki display of tags.  In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler.  However, this little box may interfere with the tables used by the task macros.  In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible.  In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box.  If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative.  Simply import it into your wiki and rename it to ViewTemplate.  This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>
-# Most view templates also add a minus sign ("-") before the "close" command.  TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out.  But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it.  It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed.  So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.
-
-----
-This ends the tutorial.  To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
-
-
-
PageTemplate
-|>|SiteTitle - SiteSubtitle|
-|>|MainMenu|
-|DefaultTiddlers<<br>><<br>><<br>>ViewTemplate<<br>><<br>>EditTemplate|SideBarOptions|
-|~|OptionsPanel|
-|~|SideBarTabs|
-|~|AdvancedOptions|
-|~|<<tiddler Configuration.SideBarTabs>>|
-
-''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
-
-ColorPalette
-
-SiteUrl
-
-
-
/***
-|Name|BetterTimelineMacro|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#BetterTimelineMacro|
-|Version|0.5 beta|
-|Requires|~TW2.x|
-!!!Description:
-A replacement for the core timeline macro that offers more features:
-*list tiddlers with only specfic tag
-*exclude tiddlers with a particular tag
-*limit entries to any number of days, for example one week
-*specify a start date for the timeline, only tiddlers after that date will be listed.
-
-!!!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!!!Syntax:
-{{{<<timeline better:true>>}}}
-''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
-
-additonal params:
-(use only the ones you want)
-{{{<<timeline better:true  onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30>>}}}
-
-''explanation of syntax:''
-onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
-excludeTag: tiddlers with this tag will not be listed.
-sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
-firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
-maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
-maxEntries: limit the total number of entries in the timeline.
-
-
-!!!History:
-*28-07-06: ver 0.5 beta, first release
-
-!!!Code
-***/
-//{{{
-// Return the tiddlers as a sorted array
-TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
-{
-          var results = [];
-          this.forEachTiddler(function(title,tiddler)
-          {
-          if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
-                        if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
-                                      results.push(tiddler);
-          });
-          if(field)
-                   results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
-          return results;
-}
-
-
-
-//this function by Udo
-function getParam(params, name, defaultValue)
-{
-          if (!params)
-          return defaultValue;
-          var p = params[0][name];
-          return p ? p[0] : defaultValue;
-}
-
-window.old_timeline_handler= config.macros.timeline.handler;
-config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
-{
-          var args = paramString.parseParams("list",null,true);
-          var betterMode = getParam(args, "better", "false");
-          if (betterMode == 'true')
-          {
-          var sortBy = getParam(args,"sortBy","modified");
-          var excludeTag = getParam(args,"excludeTag",undefined);
-          var includeTag = getParam(args,"onlyTag",undefined);
-          var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
-          var firstDayParam = getParam(args,"firstDay",undefined);
-          var firstDay = (firstDayParam!=undefined)? firstDayParam: "00010101";
-          var lastDay = "";
-          var field= sortBy;
-          var maxDaysParam = getParam(args,"maxDays",undefined);
-          var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
-          var maxEntries = getParam(args,"maxEntries",undefined);
-          var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
-          for(var t=tiddlers.length-1; t>=last; t--)
-                  {
-                  var tiddler = tiddlers[t];
-                  var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
-                  if ((theDay>=firstDay)&& (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
-                     {
-                     if(theDay != lastDay)
-                               {
-                               var theDateList = document.createElement("ul");
-                               place.appendChild(theDateList);
-                               createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
-                               lastDay = theDay;
-                               }
-                  var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
-                  theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
-                  }
-                  }
-          }
-
-          else
-              {
-              window.old_timeline_handler.apply(this,arguments);
-              }
-}
-//}}}
-
-
-
! Programming Languages
-* C
-** a C99 compatible compiler, some GCC extensions are used, most are optional.
-* C++
-** C++98
-** std::tr1 (for <std::tr1::memory>)
-* BOOST ~~(listed below are the DEBIAN package names)~~
-** libboost-dev (>=1.34.1-2)
-** libboost-program-options-dev (>=1.34.1-2)
-** libboost-program-options1.34.1 (>=1.34.1-2) ''NOTE: binary dependency''
-** libboost-regex-dev (>=1.34.1-2)
-** libboost-regex1.34.1 (>=1.34.1-2) ''binary..''
-** //usually, newer versions are OK//
-
-* bash
-  some build scripts (test.sh, ..) use bash specific extensions
-
-! Build Tools
-* autotools
-* SCons
-** //need either autotools or scons//
-** SCons (0.96), Python (2.4)
-** pkg-config
-* Doxygen
-* test.sh (included)
-
-! Extra tools
-* git
-* bouml
-
-! Libraries
-* boost (see above, version 1.35 works too)
-* NoBug
-* [[GAVL|http://gmerlin.sourceforge.net/gavl.html]] (1.0.0) 
-* for the GUI: gtkmm-2.4 gdl-1.0 libglibmm-2.4 cairomm-1.0 xv
-** libgtkmm-2.4-dev (>=2.8)
-** libcairomm-1.0-dev (>=0.6.0)
-** libgdl-lum-dev or libgdl-1-dev (>=2.27.1)
-*** libxml2-dev (>=2.6)
-** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16) and gthread-2.0 (>=2.12.4)
-** libxv-dev ~~(1.0.2 is known to work)~~
-* for rendering the icons: librsvg-2.0 
-** librsvg2-dev (>= 2.18)
-
-//usually, newer versions are OK//
-
-! Special Library requirements
-We use the GNOME Docking Library (GDL) within the Lumiera GTK GUI. As we actively participate in GDL development, we depend on a much more recent version, than most distos provide. We maintain a custom debian package (and source tree of GDL) which builds a {{{libgdl-lum.so}}}, in order to avoid side effects on other software. You may grab the tree including the Debian source package structure from [[our git|http://git.lumiera.org/gitweb?p=gdl-package;a=shortlog;h=refs/heads/debLumiera]] and create a binary Debian (or Ubuntu) package. For non-Debian systems, you may use the same build tree just in the standard way (configure, make, make install) to install into {{{/usr/local/lib/libgdl-lum.so}}}. Alternatively you may of course always just use a recent snapshot of GDL and build and install it as usual &mdash; but doing so may cause other software on your system to use this bleeding edge version of GDL too.
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #aaf
-PrimaryLight: #88f
-PrimaryMid: #00d
-PrimaryDark: #004
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
Overview
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
! Platform
-We work and test on PC hardware, 32 and 64 bit. It is intended that Lumiera runs on other platforms which run GNU/Linux.
-
-! Graphics
-There are no special requirements for the graphic system, using of OpenGL will likely be defined by the single plugins. Anyways it will be strictly optional.
-
-! Disks
-Video editing requires decent disk speed, it is suggested to use a fast/big array of disks configured as raid.
-
-! Special Hardware
-There are currently no plans to add support for propietary hardware accelerator boards. We don't have such hardware and we don't want to tie a free software program to closed hardware. This might change if the benefits are worth it, someone dontates such hardware to the developers, the specs of the hardware and the programming API are open.
-
-
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
-
/***
-|''Name:''|InlineJavascriptPlugin|
-|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
-|''Author:''|Eric Shulman - ELS Design Studios|
-|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
-|''~CoreVersion:''|2.0.10|
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Display script source in tiddler output''
-By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-><script show>
- alert('InlineJavascriptPlugin: this is a demonstration message');
-</script>
-dynamic output:
-><script show>
- return (new Date()).toString();
-</script>
-wikified dynamic output:
-><script show>
- return "link to current user: [["+config.options.txtUserName+"]]";
-</script>
-dynamic output using 'place' to get size information for current tiddler:
-><script show>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-creating an 'onclick' button/link that runs a script:
-><script label="click here" show>
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-loading a script from a source url:
->http://www.TiddlyTools.com/demo.js contains:
->>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
->>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
-><script src="demo.js" show>
- return "loading demo.js..."
-</script>
-><script label="click to execute demo() function" show>
- demo()
-</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
-''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
-''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]'' initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[4]) { // there is script code
- if (lookaheadMatch[3]) // show inline script code in tiddler output
- wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
- if (lookaheadMatch[2]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[4]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else { // run inline script code
- var code="function _out(place){"+lookaheadMatch[4]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
- }
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
''[[Lumiera|index.html]]''
-OperatingSystem
-[[Hardware]]
-[[Software]]
-BuildDependencies
-RuntimeDependencies
-StyleGuide
-[[Admin]]
-<<fullscreen>>
-
-
-
<!--{{{-->
-<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
-<!--}}}-->
-
-<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>My TiddlyWiki</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>
-
-
-
Lumiera is written for GNU/Linux. We try to make the best out of modern system programming techniques to reach the best possible performance. It is suggested to use a 64bit OS. Lumiera shall scale with the provided Hardware, the more RAM and the more/faster CPU's you have the better. Nevertheless lower end 32bit machines are supported too.
-
-Secondary targets will be other free operating systems which offer a decent Posix API. By coincidence it will be possible to port Lumiera to Mac OSX if anyone helps with that.
-
-Windows platforms are not on our target list, if someone wants to do port it, we will merge his efforts but unless that happens we will not care for windows compatibility.
-
-
-
-
 * OperatingSystem
- * [[Hardware]]
- * [[Software]]
- * BuildDependencies
- * RuntimeDependencies
- * StyleGuide
-
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-	<div class='headerShadow'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-	<div class='headerForeground'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-</div>
-<!-- horizontal MainMenu -->
-<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
-<!-- original MainMenu menu -->
-<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
-<div id='sidebar'>
-	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-	<div id='messageArea'></div>
-	<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
-
/***
-|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
-|''Version:''|1.0.6 (2006-11-07)|
-|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
-|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
-|''Licence:''|[[BSD open source license]]|
-|''TiddlyWiki:''|2.0|
-|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
-!Table of Content<html><a name="TOC"/></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
-!Description<html><a name="Description"/></html>
-With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
-Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts, use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
-
-''Syntax:'' 
-|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
-|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//.|
-|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
-|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
-|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Applications<html><a name="Applications"/></html>
-!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
-Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
-
-Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Citation Index<html><a name="Citation"/></html>
-Create a tiddler "Citations" that contains your "citations". 
-Wrap every citation with a part and a proper name. 
-
-''Example''
-{{{
-<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
-in //Proc. ICSM//, 1998.</part>
-
-<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
-Thesis, Uni Stuttgart, 2002.</part>
-
-<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
-in //Proc. ICSM//, 1999.</part>
-}}}
-
-You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
-You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
-{{{
-* Item 1
-* Item 2
-* Item 3
-}}}
-into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
-
-Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
-
-''Example''
-{{{
-|!Subject|!Items|
-|subject1|<<tiddler ./Cell1>>|
-|subject2|<<tiddler ./Cell2>>|
-
-<part Cell1 hidden>
-* Item 1
-* Item 2
-* Item 3
-</part>
-...
-}}}
-
-Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
-
-BTW: The same approach can be used to create bullet lists with items that contain more than one line.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating Tabs<html><a name="Tabs"/></html>
-The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
-
-With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
-
-''Example''
-The standard tabs at the sidebar are defined by the following eight tiddlers:
-* SideBarTabs
-* TabAll
-* TabMore
-* TabMoreMissing
-* TabMoreOrphans
-* TabMoreShadowed
-* TabTags
-* TabTimeline
-
-Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
-{{{
-<<tabs txtMainTab 
- Timeline Timeline SideBarTabs/Timeline 
- All 'All tiddlers' SideBarTabs/All 
- Tags 'All tags' SideBarTabs/Tags 
- More 'More lists' SideBarTabs/More>>
-<part Timeline hidden><<timeline>></part>
-<part All hidden><<list all>></part>
-<part Tags hidden><<allTags>></part>
-<part More hidden><<tabs txtMoreTab 
- Missing 'Missing tiddlers' SideBarTabs/Missing 
- Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
- Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
-<part Missing hidden><<list missing>></part>
-<part Orphans hidden><<list orphans>></part>
-<part Shadowed hidden><<list shadowed>></part>
-}}}
-
-Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
-
-E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
-{{{
-<<forEachTiddler 
- sortBy 'tiddler.modified' descending 
- write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
-}}}
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Using Sliders<html><a name="Sliders"/></html>
-Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
-
-''Example''
-In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
-{{{
-...
-<<slider chkAboutDetails About/Details details "Click here to see more details">>
-<part Details hidden>
-To give you a better overview ...
-</part>
-...
-}}}
-
-Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Revision history<html><a name="Revisions"/></html>
-* v1.0.6 (2006-11-07)
-** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
-* v1.0.5 (2006-03-02)
-** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
-* v1.0.4 (2006-02-28)
-** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
-* v1.0.3 (2006-02-26)
-** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
-* v1.0.2 (2006-02-05)
-** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
-* v1.0.1 (2006-01-27)
-** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
-** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
-* v1.0.0 (2006-01-25)
-** initial version
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Code<html><a name="Code"/></html>
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-//{{{
-//============================================================================
-// PartTiddlerPlugin
-
-// Ensure that the PartTiddler Plugin is only installed once.
-//
-if (!version.extensions.PartTiddlerPlugin) {
-
-
-
-version.extensions.PartTiddlerPlugin = {
- major: 1, minor: 0, revision: 6,
- date: new Date(2006, 10, 7), 
- type: 'plugin',
- source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
-};
-
-if (!window.abego) window.abego = {};
-if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
-
-//============================================================================
-// Common Helpers
-
-// Looks for the next newline, starting at the index-th char of text. 
-//
-// If there are only whitespaces between index and the newline 
-// the index behind the newline is returned, 
-// otherwise (or when no newline is found) index is returned.
-//
-var skipEmptyEndOfLine = function(text, index) {
- var re = /(\n|[^\s])/g;
- re.lastIndex = index;
- var result = re.exec(text);
- return (result && text.charAt(result.index) == '\n') 
- ? result.index+1
- : index;
-}
-
-
-//============================================================================
-// Constants
-
-var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
-var partEndTagREString = "<\\/part>";
-var partEndTagString = "</part>";
-
-//============================================================================
-// Plugin Specific Helpers
-
-// Parse the parameters inside a <part ...> tag and return the result.
-//
-// @return [may be null] {partName: ..., isHidden: ...}
-//
-var parseStartTagParams = function(paramText) {
- var params = paramText.readMacroParams();
- if (params.length == 0 || params[0].length == 0) return null;
- 
- var name = params[0];
- var paramsIndex = 1;
- var hidden = false;
- if (paramsIndex < params.length) {
- hidden = params[paramsIndex] == "hidden";
- paramsIndex++;
- }
- 
- return {
- partName: name, 
- isHidden: hidden
- };
-}
-
-// Returns the match to the next (end or start) part tag in the text, 
-// starting the search at startIndex.
-// 
-// When no such tag is found null is returned, otherwise a "Match" is returned:
-// [0]: full match
-// [1]: matched "end" tag (or null when no end tag match)
-// [2]: matched "start" tag (or null when no start tag match)
-// [3]: content of start tag (or null if no start tag match)
-//
-var findNextPartEndOrStartTagMatch = function(text, startIndex) {
- var re = new RegExp(partEndOrStartTagRE);
- re.lastIndex = startIndex;
- var match = re.exec(text);
- return match;
-}
-
-//============================================================================
-// Formatter
-
-// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
-//
-// @return true if a complete part section (including the end tag) could be processed, false otherwise.
-//
-var handlePartSection = function(w) {
- var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
- if (!tagMatch) return false;
- if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
-
- // Parse the start tag parameters
- var arguments = parseStartTagParams(tagMatch[3]);
- if (!arguments) return false;
- 
- // Continue processing
- var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
- var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
- if (endMatch && endMatch[1]) {
- if (!arguments.isHidden) {
- w.nextMatch = startTagEndIndex;
- w.subWikify(w.output,partEndTagREString);
- }
- w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
- 
- return true;
- }
- return false;
-}
-
-config.formatters.push( {
- name: "part",
- match: "<part\\s+[^>]+>",
- 
- handler: function(w) {
- if (!handlePartSection(w)) {
- w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
- }
- }
-} )
-
-//============================================================================
-// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
-// as tiddlers.
-
-var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
-
-// Return the match to the first <part ...> tag of the text that has the
-// requrest partName.
-//
-// @return [may be null]
-//
-var findPartStartTagByName = function(text, partName) {
- var i = 0;
- 
- while (true) {
- var tagMatch = findNextPartEndOrStartTagMatch(text, i);
- if (!tagMatch) return null;
-
- if (tagMatch[2]) {
- // Is start tag
- 
- // Check the name
- var arguments = parseStartTagParams(tagMatch[3]);
- if (arguments && arguments.partName == partName) {
- return tagMatch;
- }
- }
- i += tagMatch[0].length;
- }
-}
-
-// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
-// object, using fullName as the Tiddler's title. 
-//
-// All remaining properties of the new Tiddler (tags etc.) are inherited from 
-// the parentTiddler.
-// 
-// @return [may be null]
-//
-var getPart = function(parentTiddler, partName, fullName) {
- var text = parentTiddler.text;
- var startTag = findPartStartTagByName(text, partName);
- if (!startTag) return null;
- 
- var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
- var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
-
- if (indexOfEndTag >= 0) {
- var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
- var partTiddler = new Tiddler();
- partTiddler.set(
- fullName,
- partTiddlerText,
- parentTiddler.modifier,
- parentTiddler.modified,
- parentTiddler.tags,
- parentTiddler.created);
- partTiddler.abegoIsPartTiddler = true;
- return partTiddler;
- }
- 
- return null;
-}
-
-// Hijack the store.fetchTiddler to recognize the "part" addresses.
-//
-
-var oldFetchTiddler = store.fetchTiddler ;
-store.fetchTiddler = function(title) {
- var result = oldFetchTiddler.apply(this, arguments);
- if (!result && title) {
- var i = title.lastIndexOf('/');
- if (i > 0) {
- var parentName = title.substring(0, i);
- var partName = title.substring(i+1);
- var parent = (parentName == ".") 
- ? currentParent 
- : oldFetchTiddler.apply(this, [parentName]);
- if (parent) {
- return getPart(parent, partName, parent.title+"/"+partName);
- }
- }
- }
- return result; 
-};
-
-
-// The user must not edit a readOnly/partTiddler
-//
-
-config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
-
-Tiddler.prototype.isReadOnly = function() {
- // Tiddler.isReadOnly was introduced with TW 2.0.6.
- // For older version we explicitly check the global readOnly flag
- if (config.commands.editTiddler.oldIsReadOnlyFunction) {
- if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
- } else {
- if (readOnly) return true;
- }
-
- return this.abegoIsPartTiddler;
-}
-
-config.commands.editTiddler.handler = function(event,src,title)
-{
- var t = store.getTiddler(title);
- // Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
- // or the tiddler is not readOnly
- if(!t || !t.abegoIsPartTiddler)
- {
- clearMessage();
- story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
- story.focusTiddler(title,"text");
- return false;
- }
-}
-
-// To allow the "./partName" syntax in macros we need to hijack 
-// the invokeMacro to define the "currentParent" while it is running.
-// 
-var oldInvokeMacro = window.invokeMacro;
-function myInvokeMacro(place,macro,params,wikifier,tiddler) {
- var oldCurrentParent = currentParent;
- if (tiddler) currentParent = tiddler;
- try {
- oldInvokeMacro.apply(this, arguments);
- } finally {
- currentParent = oldCurrentParent;
- }
-}
-window.invokeMacro = myInvokeMacro;
-
-// Scroll the anchor anchorName in the viewer of the given tiddler visible.
-// When no tiddler is defined use the tiddler of the target given event is used.
-window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
- var tiddlerElem = null;
- if (tiddler) {
- tiddlerElem = document.getElementById(story.idPrefix + tiddler);
- }
- if (!tiddlerElem && evt) {
- var target = resolveTarget(evt);
- tiddlerElem = story.findContainingTiddler(target);
- }
- if (!tiddlerElem) return;
-
- var children = tiddlerElem.getElementsByTagName("a");
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var name = child.getAttribute("name");
- if (name == anchorName) {
- var y = findPosY(child);
- window.scrollTo(0,y);
- return;
- }
- }
-}
-
-} // of "install only once"
-//}}}
-
-/***
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Licence and Copyright
-Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-Neither the name of abego Software nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
//last update: RSSReaderPlugin v 1.1.1//
-
-!Description
-This plugin provides a RSSReader for TiddlyWiki
-* It accesses asynchronously an RSSFeed
-*Depending on the chanel item format, each item could be written as :
-**simple text wikified
-**html
-
-!Usage
-{{{
-<<rssReader noDesc|asHtml|asText rssUrl ['filtering string']>>
-	noDesc: only title of item is printed
-
-	asHtml: if you know that description contain html (links, img ...), 
-		the text is enclosed with <html> </html> tags
-
- 	asText: if the description should not be interpreted as html the 
-		description is wikified
-
-	rssUrl: the rssFeed url that could be accessed. 
-	
-	'filtering string': if present, the rssfeed item title must contained 
-		this string to be displayed. 
-		If 'filering string' contained space characters only, the tiddler 
-		title is used for filtering.
-
-}}}
-
-For security reasons, if the TiddlyWiki is accessed from http, a ProxyService should be used to access an rssFeed from an other site.
-
-!examples
-| !reader | !RSSFeed type | !working from |
-| BidiXTWRSS | Description asHtml | file: or tiddlywiki.bidix.info |
-| [[Le Monde]] | Description asText | file: or tiddlywiki.bidix.info using proxy |
-| YahooNewsSport | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| TiddlyWikiRSS | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| [[Libération]] | noDesc | file: or tiddlywiki.bidix.info using proxy |
-| [[TestComment]] | asText and filters | file: or tiddlywiki.bidix.info using proxy |
-see : <<tag RSSFeed>> for the full list.
-
-!Revision history
-* V1.1.0 (2207/04/13)
-**No more import functions
-* V1.0.0 (2006/11/11)
-**refactoring using core loadRemoteFile function
-**import using new tiddlywiki:tiddler element
-**import and presentation preserved without EricShulman's NestedSliderPlugin
-**better display of items 
-* v0.3.0 (24/08/2006)
-** Filter on RSS item title
-** Place to display redefined for asynchronous processing
-* v0.2.2 (22/08/2006)
-**Haloscan feed has no pubDate.
-* v0.2.1 (08/05/2006)
-* v0.2.0 (01/05/2006)
-**Small adapations for del.icio.us feed
-* v0.1.1 (28/04/2006)
-**Bug : Channel without title 
-* v0.1.0 (24/04/2006)
-** initial release
-
-
-
-
-
-
Dependencies and Style
-
-
-
Compatibility
-
-
-
Lumiera expects a 'standard' desktop installation running a Xserver.
-
-
-
-
/***
-
-''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
-
-|Name|SplashScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#SplashScreenPlugin|
-|Version|0.21 |
-|Requires|~TW2.08+|
-!Description:
-Provides a simple splash screen that is visible while the TW is loading.
-
-!Installation
-Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
-
-!Customizing
-Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
-
-!History
-* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
-* 26-06-06 : version 0.2, first release
-
-!Code
-***/
-//{{{
-var old_lewcid_splash_restart=restart;
-
-restart = function()
-{   if (document.getElementById("SplashScreen"))
-        document.getElementById("SplashScreen").style.display = "none";
-      if (document.getElementById("contentWrapper"))
-        document.getElementById("contentWrapper").style.display = "block";
-    
-    old_lewcid_splash_restart();
-   
-    if (splashScreenInstall)
-       {if(config.options.chkAutoSave)
-			{saveChanges();}
-        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
-        }
-}
-
-
-var oldText = store.getTiddlerText("MarkupPreHead");
-if (oldText.indexOf("SplashScreen")==-1)
-   {var siteTitle = store.getTiddlerText("SiteTitle");
-   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
-   if (! store.tiddlerExists("MarkupPreHead"))
-       {var myTiddler = store.createTiddler("MarkupPreHead");}
-   else
-      {var myTiddler = store.getTiddler("MarkupPreHead");}
-      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
-      store.setDirty(true);
-      var splashScreenInstall = true;
-}
-//}}}
-
-
-
! Source code formatting
-We decided to use the 'gnu-style' for indenting (using spaces and never tabs!):
- http://www.gnu.org/prep/standards/html_node/Formatting.html#Formatting
-
-It is reasonable to be relaxed about soem formatting rules:
- * line length might be longer when required
- * inter expession spacing can be changed to the actual needed ex: (2*x + 2)
-
-Things we are pedantic about:
- * use never ever tab characters in C/C++ sources
- * be consistent
- * source files should end with a newline
- * no trailing or bogus whitespaces
-
-! Coding Practices
-
-! Writing Tests
-
-! Contibuting
-
-
-
-
/*{{{*/
-/* a contrasting background so I can see where one tiddler ends and the other begins */
-body {
-	background: [[ColorPalette::TertiaryLight]];
-}
-
-/* sexy colours and font for the header */
-.headerForeground {
-	color: [[ColorPalette::PrimaryPale]];
-}
-.headerShadow, .headerShadow a {
-	color: [[ColorPalette::PrimaryMid]];
-}
-.headerForeground, .headerShadow {
-	padding: 1em 1em 0;
-	font-family: 'Trebuchet MS' sans-serif;
-	font-weight:bold;
-}
-.headerForeground .siteSubtitle {
-	color: [[ColorPalette::PrimaryLight]];
-}
-.headerShadow .siteSubtitle {
-	color: [[ColorPalette::PrimaryMid]];
-}
-
-/* make shadow go and down right instead of up and left */
-.headerShadow {
-	left: 2px;
-	top: 3px;
-}
-
-/* prefer monospace for editing */
-.editor textarea {
-	font-family: 'Consolas' monospace;
-}
-
-/* sexy tiddler titles */
-.title {
-	font-size: 250%;
-	color: [[ColorPalette::PrimaryLight]];
-	font-family: 'Trebuchet MS' sans-serif;
-}
-
-/* more subtle tiddler subtitle */
-.subtitle {
-	padding:0px;
-	margin:0px;
-	padding-left:0.5em;
-	font-size: 90%;
-	color: [[ColorPalette::TertiaryMid]];
-}
-.subtitle .tiddlyLink {
-	color: [[ColorPalette::TertiaryMid]];
-}
-
-/* a little bit of extra whitespace */
-.viewer {
-	padding-bottom:3px;
-}
-
-/* don't want any background color for headings */
-h1,h2,h3,h4,h5,h6 {
-	background: [[ColorPalette::Background]];
-	color: [[ColorPalette::Foreground]];
-}
-
-/* give tiddlers 3d style border and explicit background */
-.tiddler {
-	background: [[ColorPalette::Background]];
-	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
-	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
-	margin-bottom: 1em;
-	padding-bottom: 2em;
-}
-
-/* make options slider look nicer */
-#sidebarOptions .sliderPanel {
-	border:solid 1px [[ColorPalette::PrimaryLight]];
-}
-
-
-/* the borders look wrong with the body background */
-#sidebar .button {
-	border-style: none;
-}
-
-/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
-.tagglyTagged li.listTitle {
-	display:none
-}
-.tagglyTagged li {
-	display: inline; font-size:90%;
-}
-.tagglyTagged ul {
-	margin:0px; padding:0px;
-}
-
-/* this means you can put line breaks in SidebarOptions for readability */
-#sidebarOptions br {
-	display:none;
-}
-/* undo the above in OptionsPanel */
-#sidebarOptions .sliderPanel br {
-	display:inline;
-}
-
-/* horizontal main menu stuff */
-#displayArea {
-	margin: 1em 15.7em 0em 1em; /* use the freed up space */
-}
-#topMenu br {
-	display: none;
-}
-#topMenu {
-	background: [[ColorPalette::PrimaryMid]];
-	color:[[ColorPalette::PrimaryPale]];
-}
-#topMenu {
-	padding:2px;
-}
-#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
-	margin-left: 0.5em;
-	margin-right: 0.5em;
-	padding-left: 3px;
-	padding-right: 3px;
-	color: [[ColorPalette::PrimaryPale]];
-	font-size: 115%;
-}
-#topMenu .button:hover, #topMenu .tiddlyLink:hover {
-	background: [[ColorPalette::PrimaryDark]];
-}
-
-/* make it print a little cleaner */
-@media print {
-	#topMenu {
-		display: none ! important;
-	}
-	/* not sure if we need all the importants */
-	.tiddler {
-		border-style: none ! important;
-		margin:0px ! important;
-		padding:0px ! important;
-		padding-bottom:2em ! important;
-	}
-	.tagglyTagging .button, .tagglyTagging .hidebutton {
-		display: none ! important;
-	}
-	.headerShadow {
-		visibility: hidden ! important;
-	}
-	.tagglyTagged .quickopentag, .tagged .quickopentag {
-		border-style: none ! important;
-	}
-	.quickopentag a.button, .miniTag {
-		display: none ! important;
-	}
-}
-/*}}}*/
-
-
-
-
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
-
- - - - - - - - - - diff --git a/wiki/index.html b/wiki/index.html index 516cf549d..874368d26 100644 --- a/wiki/index.html +++ b/wiki/index.html @@ -498,127 +498,6 @@ Also see AdvancedOptions
-
-
A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far.  Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:
-<<<
-<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.
-
-Try modifying the time spent.  Suppose you've just spent one more hour and want to record it.  Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window.  Watch the time remaining go down to 1 hour.
-
-In reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours.  The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:
-<<<
-<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-You can adjust the current estimate in the same way as you adjusted the time spent.  Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".
-
-You can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases).  Click on the time remaining and add an hour by typing "+1".
-
-When the time remaining goes to zero, the task is considered complete:
-<<<
-<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you haven't already done so, try double-clicking the description.  Yes, it really does open up the editor and select just the text of the description.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 0.5>> Put tasksum on the ViewTemplate.
-<<<
-You can specify just the description of a task, and leave it unestimated.  Click the question mark to enter the estimate:
-<<<
-<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-As this task implies, you can enter two values in the popup when you click on any of the time cells.  Separate them with spaces and/or a comma.  Experiment:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-Finally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there.  That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.
-
-And setting up a taskadder is simplicity itself.  Just add {{{<<taskadder>>}}} to your tiddler.  You will see this:
-<<<
-<<taskadder>>
-<<<
-Just type a task description into the first field, and your initial estimate for how long it will take into the second field.  Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler.  Notice that you can just start typing a new task as soon as you're done entering the first one.
-
-You can have as many taskadders as you like in any tiddler.  The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse.  Try adding some tasks here and in the above adder:
-<<<
-<<taskadder>>
-<<<
-Notice that the one you just used takes focus when this tiddler is redisplayed.
-
-A taskadder by default adds tasks above itself.  You can make it add them below by adding a {{{below}}} argument to the macro call:
-<<<
-<<taskadder below>>
-<<<
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
In this tutorial, we've been looking mostly at individual tasks.  In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler.  In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.
-
-To get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them.  Here's an example:
-<<<
-<<tasksum start>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end>>
-<<<
-If you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.
-<<<
-<<tasksum start here>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<tasksum end>>
-<<<
-You can nest these things if you like, just be sure to match starts and ends:
-<<<
-<<tasksum start here>>
-* Time cell manipulation:<<tasksum start>>
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<tasksum end "Cell manipulation:">>
-<<br>>
-* Double-click handling:<<tasksum start>>
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end "Double-clicks:">>
-
-<<tasksum end>>
-<<<
-Finally, the simplest way to use tasksum is to add it to your view template.  See TaskSummaryViewTemplate for an example template.  Note that if no tasks are present between the start and end, nothing is displayed.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort.  However, there are two issues that may affect you.  (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)
-# The task macros don't play nicely with the default TiddlyWiki display of tags.  In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler.  However, this little box may interfere with the tables used by the task macros.  In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible.  In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box.  If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative.  Simply import it into your wiki and rename it to ViewTemplate.  This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>
-# Most view templates also add a minus sign ("-") before the "close" command.  TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out.  But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it.  It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed.  So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.
-
-----
-This ends the tutorial.  To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
-
PageTemplate
 |>|SiteTitle - SiteSubtitle|
@@ -635,27 +514,6 @@ ColorPalette
 
 SiteUrl
-
-
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
@@ -828,156 +686,6 @@ 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)
-
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
to make the admin/git_hooks/post-commit working add following to your .gitconfig:
-{{{
-[alias]
-        sign = tag -s -f -m 'automatic generated on last commit'
-        publish = push --all public
-}}}
-
-these two commands are used by 'admin/git-hooks/post-commit'
-
-'git sign' creates a gpg-signed tag after each commit, named '$~BRANCH_signature' overriding an older tag of the same name. Thus the head revision is always gpg signed (it is not perfect, in some cases like some merges and other things the signature can become unsynced and needs to be fixed manually).
-
-'git publish' just sends the commit to some repository which has to be registered with 'git remote add public ...', in case you are working offline this will stuck and timeout, you may break it with ctrl-c, someone may fix it.
-
-
-
some ''interesting Branches''
-
-|![[pipapo.org|PipapoOrg]]                |!''mirrored''              |!|!description                          |
-| ct#master                               | ichthyo#master            | |Lumiera main development line         |
-| ichthyo#scons                           |                           | |[[SCons]]-based build system, improvements|
-
-
-
-
-
I use some GitAliases to make signing and publishing easier.
-
-the '.git' dir itself is not versioned/distributed since it usually contains site-specific things. Despite this we might want to distribute some maintenance scripts and hooks so I put the default hooks into admin/git_hooks/ and users can symlink from .git/hooks them when needed.
-
-For now I hope this approach suffices, maybe we need admin/git_hooks/$HOOKNAME.$USER at some point when it turns out that people want personal hooks.
-
-&rarr; see [[Interesting Branches|GitBranches]]
-
-
-
-
This part should explain what you have to do in order to download the sources of Lumiera from pipapo.org. It treats as well how to deliver improvements to cin3 using git. Git is a tool similar to cvs or svn.
-
-The cin3 sources are stored in a git repository. Maybe you have never heard of git before, but i can allay your fears: using git isn't that complicated. To download cin3 the very first time you simply have to clone a git-repo from pipapo.org. To do so just type the following in a shell:
-!!!Code
-//{{{
-git clone git://git.pipapo.org/lumiera/ct YOURCOPY'sNAME
-//}}}
-
-This will create a directory named YOURCOPY'sNAME which contains the whole data of the lumiera/ct branch of Lumiera. After having downloaded a cin3-git-repository you can update it by simply typing:
-!!!Code
-//{{{
-cd /your/path/YOURCOPY'sNAME
-git pull
-//}}}
-
-If everything went well your copy is now up to date. Pulling is faster than cloning it again and causes less traffic at the server. 
-
-!!!Tips: 
-* NEVER delete a git repository without a permission of a pro.
-* Check the url.
-* Install git before calling git
-
-Now you could compile the source code or improve Lumiera or the documentation. After having done so (let's say you have written a patch) you can deliver your changes by:
-* at first setting your name and email
-* committing your changes
-* pushing your local git-repo to a public server
-
-!!!Code
-//{{{
-git config --global user.name "YOUR REALNAME"
-git config --global user.email ~YOUR-E@MAIL.ADRESS
-git commit -m 'YOUR DESCRIPTION' -- FILE/TO/COMMIT
-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
 
@@ -2096,171 +1804,6 @@ __cehteh__ and __ichthyo__
 [2008-06-05 19:58:10] <ichthyo> :-o
 }}}
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
/***
 |''Name:''|InlineJavascriptPlugin|
@@ -2407,44 +1950,7 @@ config.formatters.push( {
 } )
 //}}}
-
-
! Lumiera design process
-A lightweight formalized process how people can add proposals for the Lumiera development.
-
-
-!! Description
-Use the Wiki at http://www.pipapo.org/pipawiki/Lumiera/DesignProcess to make it easy to add proposals in a well defined manner.
-
-I'd like to introduce a slightly formalized process for the ongoing Lumiera planning:
-* Every proposal is instantiated as 'Idea', the author gives other people the opportunity to review and comment on it with extreme prejudice, while still working out details.
-* When the the 'Idea' in a proper form and worked out in most details it becomes a 'Draft'. This 'Draft' need to be carefully reviewed, commented, perhaps corrected and rated by the other Developers.
-* At some point we may decide that a 'Draft' becomes a 'Final' (I leave it open how this decision shall be done for now). 'Final' Documents will be imported into the repository (this wiki, you are reading such a Document right now!).
-
-* Sometimes proposals will become dropped for some reason, this is indicated by changing their state to 'Dropped', they still stay in the system for further reference.
-
-!!! Pros
-* simple
-* flexible
-* no much rules
-* persistent and at Final stage well documented process
-
-!!! Cons
-* could be abused/vandalized (but wiki can use ACL's)
-* depends on my server, this might be unfavorable or unreliable, ymmv.
-* will only work if all or almost all involved people agree on this process
-
-!!! Alternatives
-We could use some forum, Trac, Mailinglist or whatever instead.
-
-Just for Design documentation I would give [[Bouml|http://bouml.free.fr/]] a try. For myself, I am not very fond of UML Design tools, while Bouml looks quite promising and we could maintain the UML model in git repositories which would be more favorable than this centralized wiki. The backside is that this needs even more agreement between the developers, everyone has to install and use Bouml (and learn its usage) and design is constrained by a external tool.
-
-This distributed wiki might be used instead the pipapo.org wiki, investigate that for future.
-
-!! Rationale
-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]]
 
 
@@ -2454,19 +1960,9 @@ Wiki works. It is simple to use and just flexible enough to handle the task. I d
 
 
 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 protocols, transcripts and decisions made there
 
 ----
-!Architecture and Subsystems
-&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 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]]
-
 !Coding &mdash; Building &mdash; Testing
 how to organize several aspects of the practical coding...
 * what to do in BOUML?                          &rarr; [[more|whatInBOUML]]
@@ -2478,16 +1974,11 @@ how to organize several aspects of the practical coding...
 * we embrace __Test Driven Development__.       &rarr; Description of [[Test System|TestSh]] and TestSuite
 
-
-
GettingStarted
-[[LumieraWiki]]
-[[Compatibility|compatibility.html]]
-[[ToDo|todo.html]]
-[[Backend|backend.html]]
-[[Proc-Layer|renderengine.html]]
-[[Support-Library|support_library.html]]
+
+
[[Proc-Layer|renderengine.html]]
+[[Lumiera.org website|http://Lumiera.org]]
 [[Admin]]
-<<fullscreen>>
+
! Manifest
@@ -3110,378 +2601,6 @@ DAMAGE.
 <html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
 ***/
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
//last update: RSSReaderPlugin v 1.1.1//
-
-!Description
-This plugin provides a RSSReader for TiddlyWiki
-* It accesses asynchronously an RSSFeed
-*Depending on the chanel item format, each item could be written as :
-**simple text wikified
-**html
-
-!Usage
-{{{
-<<rssReader noDesc|asHtml|asText rssUrl ['filtering string']>>
-	noDesc: only title of item is printed
-
-	asHtml: if you know that description contain html (links, img ...), 
-		the text is enclosed with <html> </html> tags
-
- 	asText: if the description should not be interpreted as html the 
-		description is wikified
-
-	rssUrl: the rssFeed url that could be accessed. 
-	
-	'filtering string': if present, the rssfeed item title must contained 
-		this string to be displayed. 
-		If 'filering string' contained space characters only, the tiddler 
-		title is used for filtering.
-
-}}}
-
-For security reasons, if the TiddlyWiki is accessed from http, a ProxyService should be used to access an rssFeed from an other site.
-
-!examples
-| !reader | !RSSFeed type | !working from |
-| BidiXTWRSS | Description asHtml | file: or tiddlywiki.bidix.info |
-| [[Le Monde]] | Description asText | file: or tiddlywiki.bidix.info using proxy |
-| YahooNewsSport | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| TiddlyWikiRSS | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| [[Libération]] | noDesc | file: or tiddlywiki.bidix.info using proxy |
-| [[TestComment]] | asText and filters | file: or tiddlywiki.bidix.info using proxy |
-see : <<tag RSSFeed>> for the full list.
-
-!Revision history
-* V1.1.0 (2207/04/13)
-**No more import functions
-* V1.0.0 (2006/11/11)
-**refactoring using core loadRemoteFile function
-**import using new tiddlywiki:tiddler element
-**import and presentation preserved without EricShulman's NestedSliderPlugin
-**better display of items 
-* v0.3.0 (24/08/2006)
-** Filter on RSS item title
-** Place to display redefined for asynchronous processing
-* v0.2.2 (22/08/2006)
-**Haloscan feed has no pubDate.
-* v0.2.1 (08/05/2006)
-* v0.2.0 (01/05/2006)
-**Small adapations for del.icio.us feed
-* v0.1.1 (28/04/2006)
-**Bug : Channel without title 
-* v0.1.0 (24/04/2006)
-** initial release
-
-
-
-
-
-
||'''State'''||''Final''||
-||'''Date'''||[[Date(2007-06-09T00:48:02Z)]]||
-||'''Proposed by'''||["ct"]||
-
-! Repository Setup
-Here we describe the Directory hierarchy and how the git repository are set up.
-
-!! Description
-Use "./admin/treeinfo.sh" to produce a annotated directory tree like this:
-{{{
-.                                       : The root dir for the Lumiera project
-./admin                                 : administrative scripts
-./admin/git_hooks                       : git hook scripts
-./build                                 : build dir
-./doc                                   : documentation
-./doc/devel                             : developer documentation, extra sources, doxygen
-./doc/devel/uml                         : Bouml generated HTML doc
-./doc/user                              : user documentation in texinfo
-./oldsrc                                : Cinelerra2 sources, added per case when needed
-./src                                   : every components source in a own subdir here
-./tests                                 : test suite
-./tests/bugs                            : tests against reported bugs
-./uml                                   : uml models, created with bouml
-./uml/lumiera                           : Lumiera UML model
-./wiki                                  : tiddlywiki for semi-persistent documentation
-}}}
-
-!! New Directories:
-
-When you need to add a new mariginally important directory please provides a file named DIR_INFO within this directory. It's first line should note the purpose of the directory in a few words (less than 40 characters). The following lines are free form description about the details. 
-
-!! Submodules:
-
-We want to use the new GIT feature of "Superprojects and Submodules" when it is ready for general use.
-Then we will transform several subtrees into separate GIT repos which will be linked to from the main
-Project (then called the "Superproject") as submodules.
-
-!!! Pros
-* because its a text-like structure, it is partially self-documenting
-* GIT is flexible and with the planned submodules it can be separated in chunks of manageable size if necessary
-
-!!! Cons
-* can get large and confusing
-* has no real "portal" or entrance point for people wanting to join
-
-!!Rationale
-Every important document, draft, text and code (including) prototypes should be checked into
-one SCM (or a set of related SCMs). This repo should be "almost everything" you need for the
-project. Because we try to use a distributed development model, every dev can/should have 
-his own copy and fed his changes back.
-
-This ''Repository approach'' avoids the problems of a central infrastructure and helps cut down
-project management time. Basically, every dev is responsible himself for getting every important
-piece of information added into "the general view of matters" in a consistent way.
-
-
-! Conclusion
-## When approbate (this proposal becomes a Final) write some conclusions about its process:
-
-
-
-! Comments
-Basically the structure is just fine.
-* maybe add a "pastebin" somewhere in the dev-documentation area?
-* i would add the source tree roots at level 2, so we can have several submodules here:
-** oldsrc
-** cin3
-** prototype
- -- ["Ichthyostega"] [[DateTime(2007-06-16T23:10:01Z)]]
-
-Draft now.
-
-Yes I left source dirs out but this sounds fine, note that with git, there is no problem to reorganize the repo (in contrast to CVS) later. We can fix things afterward when we find better ways.
- -- ["ct"] [[DateTime(2007-06-17T17:36:46Z)]]
-
-Whats prototype for? won't that be better a branch?
- -- ["ct"] [[DateTime(2007-06-17T22:04:39Z)]]
-
-I just wanted to show there could be additional things beside the main tree (later to be separate submodules). The example was meant as a classical
-throwaway prototype. But I agree, in our case we just start hacking at the new tree and make feature/tryout/prototype branches...
-
-The point I wanted to make is: every directory 2 levels deep in the source tree, e.g. /src/cinelerra3 or /src/oldsrource should be a completely 
-self-contained tree which can be built without needing anything of the rest of the repo. Thats an prerequisite for moving to Submodules IMHO.
-But you seem rather to put the sourcetree-roots 1 level deep. As we have just two trees at the moment (and can easily reorganize), I have no
-objections against this. The only point I really care is to try to keep the source tree self-contained without any dependencies to the rest
-of the "design GIT" (because of this Superprojects-Submodules thing...)
- -- ["Ichthyostega"] [[DateTime(2007-06-17T23:45:06Z)]]
-
-we could make the trees deeper than one level, I didn't intended 1-level depth. but also be careful with that not to make it too complex. While I am not sure if we want a complete oldsrc, that just adds weight and confusion for now (lets see). Neither I am fully decided about the hierarchy in /src (want /libs /plugins or /src/libs /src/plugins or /src/render/plugins? name it rather 'effects' than 'plugins'?). While I am quite sure that I want to separate /oldssrc and /src quite much (in /src should only be new stuff or stuff which is carefully reviewed, with know license and author).
- -- ["ct"] [[DateTime(2007-06-18T08:38:43Z)]]
-
-I made this proposal 'final' now further details are likely better worked out in the git repository (and we already started to define things there) see ./admin/treeinfo.sh 
- -- ["ct"] [[DateTime(2007-06-27T16:01:52Z)]]
-
-
[SCons|http://www.scons.org/] is an //alternate build system// written in Python and using specific python-scripts for defining the buildprocess. These build scripts, called {{{SConstruct}}} and {{{SConsscript}}} are indeed //definitions//, not scripts for //doing// the build. If you are new to SCons (and familiar with make), you should really read the [Introduction of the users guide|http://www.scons.org/doc/0.97/HTML/scons-user/book1.html], because SCons is quite a different beast then make and the autotools.
 
@@ -3580,10 +2699,6 @@ if (oldText.indexOf("SplashScreen")==-1)
 }
 //}}}
-
-
* add a ''~DIR_INFO'' file to each marginally important directory. The first line should give a short abstract about this dir (40 characters, not more), following lines can give more precise information. There is a 'admin/treeinfo.sh' script which generates a texual overview of the directory tree.
-
-
/*{{{*/
 /* a contrasting background so I can see where one tiddler ends and the other begins */
@@ -3748,902 +2863,6 @@ h1,h2,h3,h4,h5,h6 {
 
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
! The Test Script
 To drive the various tests, we use the script {{{tests/test.sh}}}. All tests are run under valgrind control if available unless {{{VALGRINDFLAGS=DISABLE}}} is defined. 
@@ -4766,133 +2985,6 @@ to help with automating the build, ichthyo would appreciate to have the followin
 //as of 18.8.2007, ichthyo has implemented this scheme for the SCons build//
 
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
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.
diff --git a/wiki/support_library.html b/wiki/support_library.html
deleted file mode 100644
index 8a6bc456b..000000000
--- a/wiki/support_library.html
+++ /dev/null
@@ -1,11816 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
My TiddlyWiki is loading ...

Requires Javascript.
- - Support Library - things we find useful - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #8cf
-PrimaryLight: #18f
-PrimaryMid: #04b
-PrimaryDark: #014
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
/*{{{*/
-body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-
-a {color:[[ColorPalette::PrimaryMid]];}
-a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
-a img {border:0;}
-
-h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
-h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
-h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
-
-.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
-.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
-.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
-
-.header {background:[[ColorPalette::PrimaryMid]];}
-.headerShadow {color:[[ColorPalette::Foreground]];}
-.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
-.headerForeground {color:[[ColorPalette::Background]];}
-.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
-
-.tabSelected{color:[[ColorPalette::PrimaryDark]];
-	background:[[ColorPalette::TertiaryPale]];
-	border-left:1px solid [[ColorPalette::TertiaryLight]];
-	border-top:1px solid [[ColorPalette::TertiaryLight]];
-	border-right:1px solid [[ColorPalette::TertiaryLight]];
-}
-.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
-.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
-.tabContents .button {border:0;}
-
-#sidebar {}
-#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
-#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
-
-.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
-.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
-.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
-	border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
-.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
-.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
-.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
-	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
-.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
-.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
-	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
-
-#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
-#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
-
-.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
-
-.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
-.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
-.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
-.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
-
-.tiddler .defaultCommand {font-weight:bold;}
-
-.shadow .title {color:[[ColorPalette::TertiaryDark]];}
-
-.title {color:[[ColorPalette::SecondaryDark]];}
-.subtitle {color:[[ColorPalette::TertiaryDark]];}
-
-.toolbar {color:[[ColorPalette::PrimaryMid]];}
-.toolbar a {color:[[ColorPalette::TertiaryLight]];}
-.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
-.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
-
-.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
-.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
-.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
-.tagging .button, .tagged .button {border:none;}
-
-.footer {color:[[ColorPalette::TertiaryLight]];}
-.selected .footer {color:[[ColorPalette::TertiaryMid]];}
-
-.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
-.sparktick {background:[[ColorPalette::PrimaryDark]];}
-
-.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
-.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
-.lowlight {background:[[ColorPalette::TertiaryLight]];}
-
-.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
-
-.imageLink, #displayArea .imageLink {background:transparent;}
-
-.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
-
-.viewer .listTitle {list-style-type:none; margin-left:-2em;}
-.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
-.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
-.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
-.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
-.viewer code {color:[[ColorPalette::SecondaryDark]];}
-.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
-
-.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
-
-.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
-.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
-.editorFooter {color:[[ColorPalette::TertiaryMid]];}
-
-#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
-#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
-#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
-#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
-.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
-.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
-#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
-/*}}}*/
-
-
-
/*{{{*/
-* html .tiddler {height:1%;}
-
-body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
-
-h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
-h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
-h4,h5,h6 {margin-top:1em;}
-h1 {font-size:1.35em;}
-h2 {font-size:1.25em;}
-h3 {font-size:1.1em;}
-h4 {font-size:1em;}
-h5 {font-size:.9em;}
-
-hr {height:1px;}
-
-a {text-decoration:none;}
-
-dt {font-weight:bold;}
-
-ol {list-style-type:decimal;}
-ol ol {list-style-type:lower-alpha;}
-ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol {list-style-type:decimal;}
-ol ol ol ol ol {list-style-type:lower-alpha;}
-ol ol ol ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol ol ol ol {list-style-type:decimal;}
-
-.txtOptionInput {width:11em;}
-
-#contentWrapper .chkOptionInput {border:0;}
-
-.externalLink {text-decoration:underline;}
-
-.indent {margin-left:3em;}
-.outdent {margin-left:3em; text-indent:-3em;}
-code.escaped {white-space:nowrap;}
-
-.tiddlyLinkExisting {font-weight:bold;}
-.tiddlyLinkNonExisting {font-style:italic;}
-
-/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
-a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
-
-#mainMenu .tiddlyLinkExisting,
-	#mainMenu .tiddlyLinkNonExisting,
-	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
-#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
-
-.header {position:relative;}
-.header a:hover {background:transparent;}
-.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
-.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
-
-.siteTitle {font-size:3em;}
-.siteSubtitle {font-size:1.2em;}
-
-#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
-
-#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
-#sidebarOptions {padding-top:0.3em;}
-#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
-#sidebarOptions input {margin:0.4em 0.5em;}
-#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
-#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
-#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
-#sidebarTabs .tabContents {width:15em; overflow:hidden;}
-
-.wizard {padding:0.1em 1em 0em 2em;}
-.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizardStep {padding:1em 1em 1em 1em;}
-.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
-.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
-.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
-.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
-
-#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
-.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
-#messageArea a {text-decoration:underline;}
-
-.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
-.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
-
-.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
-.popup .popupMessage {padding:0.4em;}
-.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
-.popup li.disabled {padding:0.4em;}
-.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
-.listBreak {font-size:1px; line-height:1px;}
-.listBreak div {margin:2px 0;}
-
-.tabset {padding:1em 0em 0em 0.5em;}
-.tab {margin:0em 0em 0em 0.25em; padding:2px;}
-.tabContents {padding:0.5em;}
-.tabContents ul, .tabContents ol {margin:0; padding:0;}
-.txtMainTab .tabContents li {list-style:none;}
-.tabContents li.listLink { margin-left:.75em;}
-
-#contentWrapper {display:block;}
-#splashScreen {display:none;}
-
-#displayArea {margin:1em 17em 0em 14em;}
-
-.toolbar {text-align:right; font-size:.9em;}
-
-.tiddler {padding:1em 1em 0em 1em;}
-
-.missing .viewer,.missing .title {font-style:italic;}
-
-.title {font-size:1.6em; font-weight:bold;}
-
-.missing .subtitle {display:none;}
-.subtitle {font-size:1.1em;}
-
-.tiddler .button {padding:0.2em 0.4em;}
-
-.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
-.isTag .tagging {display:block;}
-.tagged {margin:0.5em; float:right;}
-.tagging, .tagged {font-size:0.9em; padding:0.25em;}
-.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
-.tagClear {clear:both;}
-
-.footer {font-size:.9em;}
-.footer li {display:inline;}
-
-.annotation {padding:0.5em; margin:0.5em;}
-
-* html .viewer pre {width:99%; padding:0 0 1em 0;}
-.viewer {line-height:1.4em; padding-top:0.5em;}
-.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
-.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
-.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
-
-.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
-.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
-table.listView {font-size:0.85em; margin:0.8em 1.0em;}
-table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
-
-.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
-.viewer code {font-size:1.2em; line-height:1.4em;}
-
-.editor {font-size:1.1em;}
-.editor input, .editor textarea {display:block; width:100%; font:inherit;}
-.editorFooter {padding:0.25em 0em; font-size:.9em;}
-.editorFooter .button {padding-top:0px; padding-bottom:0px;}
-
-.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
-
-.sparkline {line-height:1em;}
-.sparktick {outline:0;}
-
-.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
-.zoomer div {padding:1em;}
-
-* html #backstage {width:99%;}
-* html #backstageArea {width:99%;}
-#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageToolbar {position:relative;}
-#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
-#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
-#backstage {position:relative; width:100%; z-index:50;}
-#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
-.backstagePanelFooter {padding-top:0.2em; float:right;}
-.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
-#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
-
-.whenBackstage {display:none;}
-.backstageVisible .whenBackstage {display:block;}
-/*}}}*/
-
-
-
/***
-StyleSheet for use when a translation requires any css style changes.
-This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
-***/
-
-/*{{{*/
-body {font-size:0.8em;}
-
-#sidebarOptions {font-size:1.05em;}
-#sidebarOptions a {font-style:normal;}
-#sidebarOptions .sliderPanel {font-size:0.95em;}
-
-.subtitle {font-size:0.8em;}
-
-.viewer table.listView {font-size:0.95em;}
-
-.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
-/*}}}*/
-
-
-
/*{{{*/
-@media print {
-#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
-#displayArea {margin: 1em 1em 0em 1em;}
-/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
-noscript {display:none;}
-}
-/*}}}*/
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-<div class='headerShadow'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-<div class='headerForeground'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-</div>
-<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
-<div id='sidebar'>
-<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-<div id='messageArea'></div>
-<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
-<div class='title' macro='view title'></div>
-<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
-<div class='tagging' macro='tagging'></div>
-<div class='tagged' macro='tags'></div>
-<div class='viewer' macro='view text wikified'></div>
-<div class='tagClear'></div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
-<div class='title' macro='view title'></div>
-<div class='editor' macro='edit title'></div>
-<div macro='annotations'></div>
-<div class='editor' macro='edit text'></div>
-<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
-<!--}}}-->
-
-
-
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
-* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
-* MainMenu: The menu (usually on the left)
-* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
-You'll also need to enter your username for signing your edits: <<option txtUserName>>
-
-
-
These InterfaceOptions for customising TiddlyWiki are saved in your browser
-
-Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
-
-<<option txtUserName>>
-<<option chkSaveBackups>> SaveBackups
-<<option chkAutoSave>> AutoSave
-<<option chkRegExpSearch>> RegExpSearch
-<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
-<<option chkAnimate>> EnableAnimations
-
-----
-Also see AdvancedOptions
-
-
- -
-
-
PageTemplate
-|>|SiteTitle - SiteSubtitle|
-|>|MainMenu|
-|DefaultTiddlers<<br>><<br>><<br>>ViewTemplate<<br>><<br>>EditTemplate|SideBarOptions|
-|~|OptionsPanel|
-|~|SideBarTabs|
-|~|AdvancedOptions|
-|~|<<tiddler Configuration.SideBarTabs>>|
-
-''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
-
-ColorPalette
-
-SiteUrl
-
-
-
/***
-|Name|BetterTimelineMacro|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#BetterTimelineMacro|
-|Version|0.5 beta|
-|Requires|~TW2.x|
-!!!Description:
-A replacement for the core timeline macro that offers more features:
-*list tiddlers with only specfic tag
-*exclude tiddlers with a particular tag
-*limit entries to any number of days, for example one week
-*specify a start date for the timeline, only tiddlers after that date will be listed.
-
-!!!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!!!Syntax:
-{{{<<timeline better:true>>}}}
-''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
-
-additonal params:
-(use only the ones you want)
-{{{<<timeline better:true  onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30>>}}}
-
-''explanation of syntax:''
-onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
-excludeTag: tiddlers with this tag will not be listed.
-sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
-firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
-maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
-maxEntries: limit the total number of entries in the timeline.
-
-
-!!!History:
-*28-07-06: ver 0.5 beta, first release
-
-!!!Code
-***/
-//{{{
-// Return the tiddlers as a sorted array
-TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
-{
-          var results = [];
-          this.forEachTiddler(function(title,tiddler)
-          {
-          if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
-                        if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
-                                      results.push(tiddler);
-          });
-          if(field)
-                   results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
-          return results;
-}
-
-
-
-//this function by Udo
-function getParam(params, name, defaultValue)
-{
-          if (!params)
-          return defaultValue;
-          var p = params[0][name];
-          return p ? p[0] : defaultValue;
-}
-
-window.old_timeline_handler= config.macros.timeline.handler;
-config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
-{
-          var args = paramString.parseParams("list",null,true);
-          var betterMode = getParam(args, "better", "false");
-          if (betterMode == 'true')
-          {
-          var sortBy = getParam(args,"sortBy","modified");
-          var excludeTag = getParam(args,"excludeTag",undefined);
-          var includeTag = getParam(args,"onlyTag",undefined);
-          var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
-          var firstDayParam = getParam(args,"firstDay",undefined);
-          var firstDay = (firstDayParam!=undefined)? firstDayParam: "00010101";
-          var lastDay = "";
-          var field= sortBy;
-          var maxDaysParam = getParam(args,"maxDays",undefined);
-          var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
-          var maxEntries = getParam(args,"maxEntries",undefined);
-          var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
-          for(var t=tiddlers.length-1; t>=last; t--)
-                  {
-                  var tiddler = tiddlers[t];
-                  var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
-                  if ((theDay>=firstDay)&& (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
-                     {
-                     if(theDay != lastDay)
-                               {
-                               var theDateList = document.createElement("ul");
-                               place.appendChild(theDateList);
-                               createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
-                               lastDay = theDay;
-                               }
-                  var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
-                  theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
-                  }
-                  }
-          }
-
-          else
-              {
-              window.old_timeline_handler.apply(this,arguments);
-              }
-}
-//}}}
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #ec5
-PrimaryLight: #ec0
-PrimaryMid: #b30
-PrimaryDark: #310
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
This hashing gives guaranteed O(1) lookup complexity and amortized O(1) insert and remove complexity. Hash tables by default grow and shrink automatically. It is posible to preallocate entries and turn automatic shrinking off taking out the memory management factors for insert and remove operations. This implementation uses 3 Tables which exponential growing sizes.
-
-Hash table utilization is commonly between 40-80% which gives better memory usage and far better locality than trees.
-
-
-
[[SupportLibrary]]
-
-
-
We distinguish the way how to cope with Errors, i.e. the ErrorHandlingPolicy, and the actual implementation
-* for code with C semantics &rarr; ErrorHandling-C
-* for C++ code &rarr; ErrorHandling-Cpp
-
-
-
! Proposal:
-We need some centralized way to handle errors and doing hard aborts.
-
-I started using C-string addresses as errors for now. I think that is convenient and unique enough until we find something better. (Actually, this might be even kept in a library. Alternatively, maybe someone wants to investigate libcomerr)
-
-Notes about libcomerr (I took a short look now):
-* needs some central error description table which has to be compiled with compile_et
-* no homepage found, some projects use it, published in 1989, dunno about its state
-
-My following proposal defines a very simplistic way to define unique errors which can distributed throughout the application (each part can define its own errors and will never interfere with others)
-
-
-!!! detailed semantics (proposal):
-a TLS pointer is allocated to keep a thread local error state. NULL means SUCCESS, no error pending.
-
-API:
-
-{{{
-const char*
-lumiera_error_set (const char * err)
-}}}
-
-if there is no error pending, then store err as new error state, if there was an error pending, then the state is not altered.
-
-return the former state (NULL if err got set, some other when an error is pending)
-
-{{{
-const char*
-lumiera_error ()
-}}}
-
-returns the error state ''and'' clears it. The user has to store it temporary when need to be used further. Rationale: less TLS access overhead, never forget to clear the state.
-
-(do we need a {{{lumiera_error_peek()}}}?)
-
-Declaration and definition:
-{{{
-#define LUMIERA_ERROR_DECLARE(err) \
-extern const char* LUMIERA_ERROR_##err
-
-#define LUMIERA_ERROR_DEFINE(err, msg) \
-const char* LUMIERA_ERROR_##err = "LUMIERA_ERROR_" #err ":" msg
-}}}
-
-thus a {{{LUMIERA_ERROR_DEFINE(NFOO, "Foo not found")}}} will result in a string:
-"~LUMIERA_ERROR_NFOO:Foo not found", having the identifier itself prepended to the string will ensure uniqueness of the generated literal (and thus its pointer value), reducing error testing to address comparison {{{lumiera_error()==LUMIERA_ERROR_NFOO}}}. The message string is easily derived by {{{strchr(LUMIERA_ERROR_NFOO, ':')+1}}}
-
-
-
-
-!! Allocation
-The next point is allocation failures. These are possible by C/C++ standard but don't actually happen anymore in Linux (except in few rare cases). Instead of gracefully handling this errors I'll add a {{{LUMIERA_DIE(message)}}} macro to this library later. This macro will just do (NoBug) logging and then doing a hard abort. It should be a macro because we want to preserve file/line location for logging.
-
-
-
-
-
Basically, the C++ error handling techniques are layered on top of the [[C solution|ErrorHandling-C]].
-!Proposal:
-We use a common base class for all our application specific exceptions. These exceptions can be thought of as a classification of error situations, thus the hierarchical approach. The purpose of throwing such a //classified exception// is
-* to do a controlled partial failure
-* to trigger automatic cleanup without the need to implement the details
-
-!!Requirements for the Exception Interface
-* a means for capturing and transporting detail informations
-* the possibility to get at the ''root cause'' of an exception, even after having passed several subsystem barriers.
-* getting standardized error messages automatically
-
-!!provided features
-The C++ errorhandling Classes and functions can be found in {{{common/error.hpp}}} (not to be confused with the elementary C errorhandling of the Lumiera support lib {{{lib/error.h}}}. See also the "exceptionerrortest.cpp"
-* the constructor of the Exception base class will set the C-style error flag as well. Obviously, when an exception gets caught and handled, this error-flag should be reset (and this is the responsibility of the handler).
-* we add a __unknown()__ handler which will print additional diagnostics.
-* the Exception class has a diagnostic message intended for developers and a friendly message for the user. It is encouraged to write a detailed description of each error situation right into a string constant passed to the exception object ctor. This will serve the purpose of documenting the error situation in the source code and at the same time help diagnosis.
-* there is a variant of the constructor taking a reference to an std::exception. This is intended for //chained exceptions//. Whenever an handler catches an exception, but then decides to rethrow it with different classification, the original exception object should be passed on by using this constructor, so it's {{{what()}}} message can be preserved and will be included in the final log entry. While this may look like overkill in a small example, it is a very helpful facility in a larger layered application, where it is often difficult to spot the original cause of an exception encountered.
-* for each mayor category of Exception subclasses, we define a C-style error constant. The client code is free to define further detailed error constants and Exception subclasses.
-* to help defining Exception subclasses, a macro {{{LUMIERA_EXCEPTION_DECLARE}}} is provided.
-
-!!!basic Exception categories
-|!category|!description|
-|error::Logic| contradiction to internal logic assumptions detected|
-|error::Fatal| special subclass of Logic: situation can't be handled, internal logic floundered |
-|error::Config| execution aborted due to misconfiguration |
-|error::State| unforeseen internal state |
-|error::Invalid| invalid input or parameters encountered |
-|error::External| failure in external service the application relies on |
-
-
-
-
! Framerates
-Framerates are stored as rational numbers eg. 30000/1001 for NTSC, this representation allows highly precise integer calculations for frames and times.
-
-! Frames
-All calculations in Lumiera are based on frames which is a signed integer type. Together with a Framerate and a starting point, every frame can be exactly located.
-
-! Time
-Time is used only internally in Lumiera, every external representation of time will be converted to frames. Time has a precision of 1 microsecond and is stored in POSIX struct timeval. Time is always handled as absolute time, thus frame addresses map to absolute times, there is at worst a 1us precision jitter but no drift.
-
-!! SMPTE and other Timecodes
-will be added on demand, but be frame based, not time based
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
-
/***
-|''Name:''|InlineJavascriptPlugin|
-|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
-|''Author:''|Eric Shulman - ELS Design Studios|
-|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
-|''~CoreVersion:''|2.0.10|
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Display script source in tiddler output''
-By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-><script show>
- alert('InlineJavascriptPlugin: this is a demonstration message');
-</script>
-dynamic output:
-><script show>
- return (new Date()).toString();
-</script>
-wikified dynamic output:
-><script show>
- return "link to current user: [["+config.options.txtUserName+"]]";
-</script>
-dynamic output using 'place' to get size information for current tiddler:
-><script show>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-creating an 'onclick' button/link that runs a script:
-><script label="click here" show>
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-loading a script from a source url:
->http://www.TiddlyTools.com/demo.js contains:
->>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
->>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
-><script src="demo.js" show>
- return "loading demo.js..."
-</script>
-><script label="click to execute demo() function" show>
- demo()
-</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
-''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
-''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]'' initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[4]) { // there is script code
- if (lookaheadMatch[3]) // show inline script code in tiddler output
- wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
- if (lookaheadMatch[2]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[4]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else { // run inline script code
- var code="function _out(place){"+lookaheadMatch[4]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
- }
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
! Provided Locking Primitives
-The support library provides wrappers around some pthread locking primitives to make their usage more robust and easier.
-
-The basic concept is that each locking primitive is an object as well as each locker is implemented as object too, this adds a small convenience layer for robustness. We use ~NoBug to assert that locks are properly unlocked.
-
-!! Mutex
-We only support fast (non recursive, non errorcheck) mutexes for now. Debugging deadlock detection will be done in ~NoBug. If we need dynamic deadlock detection we will have to support errorcheck mutexes at demand. Same for recursive mutexes.
-
-!! Condition Variables
-Condition variables are simple synchronization devices, refer to the doxygen docs about using them. One needs to lock them when preparing to wait on them and finally unlock them. While signaling can optionally be done without a locker object (locking is implicit then).
-
-!! Read/Write Locks
-Similar to mutexes we support multiple-reader/one-writer locks, they can be used whenever many concurrent read accesses and rare write accesses are expected to some datastructure (profile this later). When congestion rather unexpected then prefer a mutex.
-
-! No Semaphores Rationale
-Semaphores have quite ugly semantics and are very hard to debug. For now (and likely forever) we will not to use them.
-
-! ~ToDo
-!! trylock and timedlock
-.. is not yet implemented but will be added in request.
-
-!! Barriers
-... will be added on request too.
-
-!! Thread Cancellation
-Thread cancellation policies are not yet finally decided, for now we consider threads uncancelable!
-
-
-
''[[Lumiera|index.html]]''
-SupportLibrary
-[[Threads and Locking]]
-[[Plugins]]
-[[ErrorHandling]]
-[[OS Services]]
-<<fullscreen>>
-
-
-
-
<!--{{{-->
-<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
-<!--}}}-->
-
-<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>My TiddlyWiki</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-	<div class='headerShadow'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-	<div class='headerForeground'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-</div>
-<!-- horizontal MainMenu -->
-<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
-<!-- original MainMenu menu -->
-<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
-<div id='sidebar'>
-	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-	<div id='messageArea'></div>
-	<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
-
/***
-|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
-|''Version:''|1.0.6 (2006-11-07)|
-|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
-|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
-|''Licence:''|[[BSD open source license]]|
-|''TiddlyWiki:''|2.0|
-|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
-!Table of Content<html><a name="TOC"/></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
-!Description<html><a name="Description"/></html>
-With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
-Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts, use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
-
-''Syntax:'' 
-|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
-|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//.|
-|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
-|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
-|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Applications<html><a name="Applications"/></html>
-!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
-Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
-
-Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Citation Index<html><a name="Citation"/></html>
-Create a tiddler "Citations" that contains your "citations". 
-Wrap every citation with a part and a proper name. 
-
-''Example''
-{{{
-<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
-in //Proc. ICSM//, 1998.</part>
-
-<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
-Thesis, Uni Stuttgart, 2002.</part>
-
-<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
-in //Proc. ICSM//, 1999.</part>
-}}}
-
-You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
-You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
-{{{
-* Item 1
-* Item 2
-* Item 3
-}}}
-into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
-
-Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
-
-''Example''
-{{{
-|!Subject|!Items|
-|subject1|<<tiddler ./Cell1>>|
-|subject2|<<tiddler ./Cell2>>|
-
-<part Cell1 hidden>
-* Item 1
-* Item 2
-* Item 3
-</part>
-...
-}}}
-
-Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
-
-BTW: The same approach can be used to create bullet lists with items that contain more than one line.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating Tabs<html><a name="Tabs"/></html>
-The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
-
-With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
-
-''Example''
-The standard tabs at the sidebar are defined by the following eight tiddlers:
-* SideBarTabs
-* TabAll
-* TabMore
-* TabMoreMissing
-* TabMoreOrphans
-* TabMoreShadowed
-* TabTags
-* TabTimeline
-
-Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
-{{{
-<<tabs txtMainTab 
- Timeline Timeline SideBarTabs/Timeline 
- All 'All tiddlers' SideBarTabs/All 
- Tags 'All tags' SideBarTabs/Tags 
- More 'More lists' SideBarTabs/More>>
-<part Timeline hidden><<timeline>></part>
-<part All hidden><<list all>></part>
-<part Tags hidden><<allTags>></part>
-<part More hidden><<tabs txtMoreTab 
- Missing 'Missing tiddlers' SideBarTabs/Missing 
- Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
- Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
-<part Missing hidden><<list missing>></part>
-<part Orphans hidden><<list orphans>></part>
-<part Shadowed hidden><<list shadowed>></part>
-}}}
-
-Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
-
-E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
-{{{
-<<forEachTiddler 
- sortBy 'tiddler.modified' descending 
- write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
-}}}
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Using Sliders<html><a name="Sliders"/></html>
-Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
-
-''Example''
-In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
-{{{
-...
-<<slider chkAboutDetails About/Details details "Click here to see more details">>
-<part Details hidden>
-To give you a better overview ...
-</part>
-...
-}}}
-
-Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Revision history<html><a name="Revisions"/></html>
-* v1.0.6 (2006-11-07)
-** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
-* v1.0.5 (2006-03-02)
-** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
-* v1.0.4 (2006-02-28)
-** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
-* v1.0.3 (2006-02-26)
-** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
-* v1.0.2 (2006-02-05)
-** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
-* v1.0.1 (2006-01-27)
-** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
-** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
-* v1.0.0 (2006-01-25)
-** initial version
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Code<html><a name="Code"/></html>
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-//{{{
-//============================================================================
-// PartTiddlerPlugin
-
-// Ensure that the PartTiddler Plugin is only installed once.
-//
-if (!version.extensions.PartTiddlerPlugin) {
-
-
-
-version.extensions.PartTiddlerPlugin = {
- major: 1, minor: 0, revision: 6,
- date: new Date(2006, 10, 7), 
- type: 'plugin',
- source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
-};
-
-if (!window.abego) window.abego = {};
-if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
-
-//============================================================================
-// Common Helpers
-
-// Looks for the next newline, starting at the index-th char of text. 
-//
-// If there are only whitespaces between index and the newline 
-// the index behind the newline is returned, 
-// otherwise (or when no newline is found) index is returned.
-//
-var skipEmptyEndOfLine = function(text, index) {
- var re = /(\n|[^\s])/g;
- re.lastIndex = index;
- var result = re.exec(text);
- return (result && text.charAt(result.index) == '\n') 
- ? result.index+1
- : index;
-}
-
-
-//============================================================================
-// Constants
-
-var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
-var partEndTagREString = "<\\/part>";
-var partEndTagString = "</part>";
-
-//============================================================================
-// Plugin Specific Helpers
-
-// Parse the parameters inside a <part ...> tag and return the result.
-//
-// @return [may be null] {partName: ..., isHidden: ...}
-//
-var parseStartTagParams = function(paramText) {
- var params = paramText.readMacroParams();
- if (params.length == 0 || params[0].length == 0) return null;
- 
- var name = params[0];
- var paramsIndex = 1;
- var hidden = false;
- if (paramsIndex < params.length) {
- hidden = params[paramsIndex] == "hidden";
- paramsIndex++;
- }
- 
- return {
- partName: name, 
- isHidden: hidden
- };
-}
-
-// Returns the match to the next (end or start) part tag in the text, 
-// starting the search at startIndex.
-// 
-// When no such tag is found null is returned, otherwise a "Match" is returned:
-// [0]: full match
-// [1]: matched "end" tag (or null when no end tag match)
-// [2]: matched "start" tag (or null when no start tag match)
-// [3]: content of start tag (or null if no start tag match)
-//
-var findNextPartEndOrStartTagMatch = function(text, startIndex) {
- var re = new RegExp(partEndOrStartTagRE);
- re.lastIndex = startIndex;
- var match = re.exec(text);
- return match;
-}
-
-//============================================================================
-// Formatter
-
-// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
-//
-// @return true if a complete part section (including the end tag) could be processed, false otherwise.
-//
-var handlePartSection = function(w) {
- var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
- if (!tagMatch) return false;
- if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
-
- // Parse the start tag parameters
- var arguments = parseStartTagParams(tagMatch[3]);
- if (!arguments) return false;
- 
- // Continue processing
- var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
- var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
- if (endMatch && endMatch[1]) {
- if (!arguments.isHidden) {
- w.nextMatch = startTagEndIndex;
- w.subWikify(w.output,partEndTagREString);
- }
- w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
- 
- return true;
- }
- return false;
-}
-
-config.formatters.push( {
- name: "part",
- match: "<part\\s+[^>]+>",
- 
- handler: function(w) {
- if (!handlePartSection(w)) {
- w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
- }
- }
-} )
-
-//============================================================================
-// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
-// as tiddlers.
-
-var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
-
-// Return the match to the first <part ...> tag of the text that has the
-// requrest partName.
-//
-// @return [may be null]
-//
-var findPartStartTagByName = function(text, partName) {
- var i = 0;
- 
- while (true) {
- var tagMatch = findNextPartEndOrStartTagMatch(text, i);
- if (!tagMatch) return null;
-
- if (tagMatch[2]) {
- // Is start tag
- 
- // Check the name
- var arguments = parseStartTagParams(tagMatch[3]);
- if (arguments && arguments.partName == partName) {
- return tagMatch;
- }
- }
- i += tagMatch[0].length;
- }
-}
-
-// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
-// object, using fullName as the Tiddler's title. 
-//
-// All remaining properties of the new Tiddler (tags etc.) are inherited from 
-// the parentTiddler.
-// 
-// @return [may be null]
-//
-var getPart = function(parentTiddler, partName, fullName) {
- var text = parentTiddler.text;
- var startTag = findPartStartTagByName(text, partName);
- if (!startTag) return null;
- 
- var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
- var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
-
- if (indexOfEndTag >= 0) {
- var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
- var partTiddler = new Tiddler();
- partTiddler.set(
- fullName,
- partTiddlerText,
- parentTiddler.modifier,
- parentTiddler.modified,
- parentTiddler.tags,
- parentTiddler.created);
- partTiddler.abegoIsPartTiddler = true;
- return partTiddler;
- }
- 
- return null;
-}
-
-// Hijack the store.fetchTiddler to recognize the "part" addresses.
-//
-
-var oldFetchTiddler = store.fetchTiddler ;
-store.fetchTiddler = function(title) {
- var result = oldFetchTiddler.apply(this, arguments);
- if (!result && title) {
- var i = title.lastIndexOf('/');
- if (i > 0) {
- var parentName = title.substring(0, i);
- var partName = title.substring(i+1);
- var parent = (parentName == ".") 
- ? currentParent 
- : oldFetchTiddler.apply(this, [parentName]);
- if (parent) {
- return getPart(parent, partName, parent.title+"/"+partName);
- }
- }
- }
- return result; 
-};
-
-
-// The user must not edit a readOnly/partTiddler
-//
-
-config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
-
-Tiddler.prototype.isReadOnly = function() {
- // Tiddler.isReadOnly was introduced with TW 2.0.6.
- // For older version we explicitly check the global readOnly flag
- if (config.commands.editTiddler.oldIsReadOnlyFunction) {
- if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
- } else {
- if (readOnly) return true;
- }
-
- return this.abegoIsPartTiddler;
-}
-
-config.commands.editTiddler.handler = function(event,src,title)
-{
- var t = store.getTiddler(title);
- // Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
- // or the tiddler is not readOnly
- if(!t || !t.abegoIsPartTiddler)
- {
- clearMessage();
- story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
- story.focusTiddler(title,"text");
- return false;
- }
-}
-
-// To allow the "./partName" syntax in macros we need to hijack 
-// the invokeMacro to define the "currentParent" while it is running.
-// 
-var oldInvokeMacro = window.invokeMacro;
-function myInvokeMacro(place,macro,params,wikifier,tiddler) {
- var oldCurrentParent = currentParent;
- if (tiddler) currentParent = tiddler;
- try {
- oldInvokeMacro.apply(this, arguments);
- } finally {
- currentParent = oldCurrentParent;
- }
-}
-window.invokeMacro = myInvokeMacro;
-
-// Scroll the anchor anchorName in the viewer of the given tiddler visible.
-// When no tiddler is defined use the tiddler of the target given event is used.
-window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
- var tiddlerElem = null;
- if (tiddler) {
- tiddlerElem = document.getElementById(story.idPrefix + tiddler);
- }
- if (!tiddlerElem && evt) {
- var target = resolveTarget(evt);
- tiddlerElem = story.findContainingTiddler(target);
- }
- if (!tiddlerElem) return;
-
- var children = tiddlerElem.getElementsByTagName("a");
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var name = child.getAttribute("name");
- if (name == anchorName) {
- var y = findPosY(child);
- window.scrollTo(0,y);
- return;
- }
- }
-}
-
-} // of "install only once"
-//}}}
-
-/***
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Licence and Copyright
-Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-Neither the name of abego Software nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-
-
-
Interfaces are declared in header files. They use some tool macros to give a convenient definition language.
-
-! Thoughts
-
-Interfaces are immutable with the exception that new functions may be added. Versioning should stay out of the view most of the time.
-
-! Brainstorming
-
-An interface needs a name and a version. They define a block where the actual function prototypes can be added. New prototypes have to be added at the end, existing prototypes must never be changed.
-{{{
-LUMIERA_INTERFACE(name, version,
-	...
-);
-}}}
-
-
-Each function prototype must be given with its different parts: return type, name, arguments list, and version.
-{{{
-	LUMIERA_IPROTO(ret, name, (args)),
-}}}
-
-
-! Example
-Together this would look like
-{{{
-LUMIERA_INTERFACE(foo, 1,
-	LUMIERA_IPROTO(void, bar, (void)),
-	LUMIERA_IPROTO(int, baz, (int i))
-);
-
-LUMIERA_INTERFACE(foo, 2,
-	LUMIERA_IPROTO(void, bar, (void)),
-	LUMIERA_IPROTO(int, baz, (float i))
-);
-}}}
-
-Note that the version 2 interface changed the parameter from int to float for the 'baz' function.
-
-The above gets expanded to:
-{{{
-struct lumiera_interface_foo_1
-{
-	struct lumiera_interface interface_header_;
-	void (*bar) (void);
-	int (*baz) (int i);
-};
-
-struct lumiera_interface_foo_2
-{
-	struct lumiera_interface interface_header_;
-	void (*bar) (void);
-	int (*baz) (float i);
-};
-}}}
-
-
-
A Plugin realizes an interface. This means that actual functions are mapped to the correspondending slots in the interface structure.
-
-{{{
-LUMIERA_INTERFACE_IMPLEMENT(interface, version, name,
-	/* TODO some hooks here */
-	functionnames...
-);
-}}}
-
-! Example
-{{{
-void
-my_bar_function (void)
-{
-	...
-}
-
-int
-my_baz_function (int i)
-{
-	...
-}
-
-int
-my_new_baz_function (float i)
-{
-	...
-}
-
-LUMIERA_INTERFACE_IMPLEMENT(foo, 1, myfoointerface,
-	/* TODO some hooks here */
-	my_bar_function,
-	my_baz_function
-);
-
-LUMIERA_INTERFACE_IMPLEMENT(foo, 2, myfoointerface,
-	/* TODO some hooks here */
-	my_bar_function,
-	my_new_baz_function
-);
-}}}
-
-The interface implementations expands to something like:
-{{{
-struct lumiera_interface_foo_1 myfoointerface_1 =
-{
-	/* TODO header initialization */
-	my_bar_function,
-	my_baz_function
-}
-
-struct lumiera_interface_foo_2 myfoointerface_2 =
-{
-	/* TODO header initialization */
-	my_bar_function,
-	my_new_baz_function
-}
-}}}
-
-
-
-
! Lumiera Plugin API
-
-There are only a few functions to manage Plugins. Actually a user requests interfaces. The libraries which implement Plugins are managed transparently.
-
-Interfaces are exported as instances and are not necessary singleton. This means that a single Plugin can export the same interface type several times under different names. The naming rules for interfaces need to be defined elsewhere.
-
-!! opening an Interface
-{{{
-LumieraInterface
-lumiera_interface_open (const char* plugin,
-                          const char* name,
-                          size_t min_revision);
-}}}
-
-!!! Parameters
-* ''{{{plugin}}}'' is the name of the Plugin whose interface to use. Plugins are looked up in $~LUMIERA_PLUGIN_PATH, which is a colon separated list of directories, and then in $plugin_install_dir which is the directory where standard plugins get installed when installing Lumiera (example: /usr/local/lib/lumiera/). The name itself can contain slashes, see PluginHierachy for details. It shall not include a library extension (.so). When NULL is passed, an interface from the main application is queried.
-* ''{{{name}}}'' is the name of the queried interface.
-* ''{{{min_revision}}}'' is the expected minimal size of the interface structure, since interfaces are extended by adding new protos at the end, the size gives a unique value for each new revision.
-
-!!! Semantic
-Interfaces can opened multiple times and need to be closed for each call to open.
-
-!!! Return
-This function returns a pointer to the requested interface on success or NULL in case of an error. See {{{lumiera_interface_error}}} about handing errors.
-
-!! closing an Interface
-{{{
-void
-lumiera_interface_close (LumieraInterface self);
-}}}
-
-!!! Parameters
-* ''{{{self}}}'' is the handle to the interface to be closed. It is safe to pass NULL. This makes the call just a no-op.
-
-!!! Semantic
-The interface handle must not be used after this function is called.
-
-This function always succeeds (or results in undefined behavior when the user passes an illegal parameter)
-
-!! calling functions
-
-Calling function is simply done by dereferencing the interface slots. See HowtoUsePlugin for an example.
-
-!! unload unused plugins
-Plugins which are no longer in use are not automatically unloaded. The user can use this functions to unload the Plugins.
-
-{{{
-int
-lumiera_plugin_unload (const char* plugin);
-}}}
-
-!!! Parameters
-* ''{{{plugin}}}'' name of the plugin to be unloaded
-
-!!! Semantic
-Tries to unload the named plugin. This only works when nothing else uses the Plugin.
-
-!!! Return
-Returns 0 on success or the number of active users on failure. See {{{lumiera_interface_error}}} about handing errors.
-
-
-!! expire unused plugins
-{{{
-void
-lumiera_plugin_expire (time_t age);
-}}}
-
-!!! Parameters
-* ''{{{age}}}'' time in seconds when the plugin was last used
-
-!!! Semantic
-Calls {{{lumiera_plugin_unload()}}} for each Plugin which has not been used for more than {{{age}}} seconds. This function might be infrequently called by the scheduler to remove things which are not needed (example: once a hour, remove plugins which have not been used for 2 hours).
-
-!!! Return
-always succeeds.
-
-!! error handling
-{{{
-const char*
-lumiera_plugin_error ();
-}}}
-
-!!! Semantic
-Indicate last error, reset error state. Errors are thread local.
-
-!!! Return
-Returns a pointer to the most recent error occurred in the plugin loader. This pointer is guaranteed to point to a C string with a unique comparable address. NULL if no error happened.
-
-Note that the error state gets cleared by calling this function. The application may store it temporary for further handling.
-
-!! C++ exceptions
-TODO
-
-
-
-
! Compatibility matrix
-
-|>|>|!Source compatibility|
-|!~~CALLER~~\^^CALLEE^^ *|OLD^^**^^|NEW^^**^^|
-|OLD|works|works<<br>>but a recent interface definition must be available|
-|NEW|works|works|
-|>|>|!Binary compatibility|
-|OLD|works|works|
-|NEW|caller gets 'revision not sufficient' at runtime<<br>>and should implement fallbacks|works|
-
-^^*^^) CALLER is the user of an interface, CALLEE is the interface provider (usually a plugin)
-^^**^^) OLD means an initial revision, NEW means some later revision of an interface
-
-! Observations
-
-Compiling a newer Plugin for some older main application release has some quirks (interface definitions are intended to be shipped with the main application). This should be rarely the case.
-
-When compiling, older Plugins should be updated to new interface revisions.
-Caller should provide a fallback to older interface revisions for binary compatibility.
-
-Generally, sources should just be properly maintained and updated to use the most recent interfaces revision.
-
-For binary compatibility everything will work well, provided that the caller kept proper fallback functionality for older interface revisions. Plugins which are independently distributed (packaged) in binary form don't need to be updated with every new main application release and just work.
-
-
-
-
-
Lumiera will use a very simple and language neutral plugin system. The focus is on easy and independent distribution of plugins and small specific interfaces. Ultimate flexibility is of second concern.
-
-! Concept
-Plugins are just shared libraries which offer well defined Interfaces. A Plugin may offer more than one interface and may in turn request/use interfaces from other Plugins or from the main application.
-
-! Interfaces
-Plugin interfaces are simple C structs with some metadata at the beginning and function prototypes added at the end. With some macros we can map simple functions to versioned interfaces. Compiled plugins will stay compatible even if the interface is extended, while sourcecode need maintenance.
-
-This fosters the idea of updating plugins when the source is available, while still having the ability to deploy packaged binary plugins which will be compatible with newer interface versions.
-
-The Plugin System is written in C with some helper preprocessor macros. There will be some support to handle C++ specialties.
-
-! Versioning
-
-Each interface/prototype is versioned. How this works together is explained in PluginVersioningCases. Version identifiers will be used to form a C identifier. I suggest to use a monotonic incrementing number, starting at 1 for versioning and maybe using a special number 0 for interfaces which are in development. When the interface development is finished the 0 has to be replaced by the next number in turn. This ensures that no-one accidentally uses/relies on an interface which is not yet well defined.
-
-! Plugin Support includes
-* [[An interface definition language|PluginInterfaceDefinition]]
-* [[An interface implementation language|PluginInterfaceImplementation]]
-* [[Selecting interface based on capabilities|SelectingInterfaces]]
-* [[Library support to access plugins|PluginLibrary]]
-
-! Tutorial, how to use Plugins
-* [[Define an interface|HowtoDefineInterface]]
-* Implement an Plugin with an interface in
-** [[C|HowtoCPlugin]]
-** [[C++|HowtoCppPlugin]]
-* [[Use this Plugin|HowtoUsePlugin]] 
-
-!! Planned
-* enumerating interfaces of a plugin
-* pattern matching interfaces -- find the best possible interface
-
-
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
! Memory allocation
-* lumiera_malloc()
-  Succceeds or dies, no need for error handling.
-
-! String functions
-Small wrapers and extensions to the string.h functions, handle NULL pointers gracefully.
-* lumiera_strndup()
-  Succceeds or dies, no need for error handling.
-* lumiera_strncmp() lumiera_streq()
-  Optimized comparing same addresses.
-
-! Round robin temporary buffers
-Provides 64 buffers per thread which are recycled with each use, the idea here is to have fast buffers for temporal data without need for explicit heap management or stack waste.
-
-* lumiera_tmpbuf_provide ()
- Acquire a temporary buffer. doesn't need to be freed. Stays valid for the next 63 tmpbuf calls.
-
-* lumiera_tmpbuf_strndup ()
- Duplicate string to a tmpbuf.
-
-* lumiera_tmpbuf_sprintf ()
- Construct a string in a tmpbuf.
-
-
-
-
Each Plugin can export different Interfaces, even same interfaces with different capabilities like a effect for different color models or such.
-
-Plugins announce their interfaces and capabilities and we implement a deduction system which chooses the best matching interface for the task.
-
-
-
-
things we find useful
-
-
-
Support Library
-
-
-
/***
-
-''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
-
-|Name|SplashScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#SplashScreenPlugin|
-|Version|0.21 |
-|Requires|~TW2.08+|
-!Description:
-Provides a simple splash screen that is visible while the TW is loading.
-
-!Installation
-Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
-
-!Customizing
-Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
-
-!History
-* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
-* 26-06-06 : version 0.2, first release
-
-!Code
-***/
-//{{{
-var old_lewcid_splash_restart=restart;
-
-restart = function()
-{   if (document.getElementById("SplashScreen"))
-        document.getElementById("SplashScreen").style.display = "none";
-      if (document.getElementById("contentWrapper"))
-        document.getElementById("contentWrapper").style.display = "block";
-    
-    old_lewcid_splash_restart();
-   
-    if (splashScreenInstall)
-       {if(config.options.chkAutoSave)
-			{saveChanges();}
-        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
-        }
-}
-
-
-var oldText = store.getTiddlerText("MarkupPreHead");
-if (oldText.indexOf("SplashScreen")==-1)
-   {var siteTitle = store.getTiddlerText("SiteTitle");
-   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
-   if (! store.tiddlerExists("MarkupPreHead"))
-       {var myTiddler = store.createTiddler("MarkupPreHead");}
-   else
-      {var myTiddler = store.getTiddler("MarkupPreHead");}
-      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
-      store.setDirty(true);
-      var splashScreenInstall = true;
-}
-//}}}
-
-
-
/*{{{*/
-/* a contrasting background so I can see where one tiddler ends and the other begins */
-body {
-	background: [[ColorPalette::TertiaryLight]];
-}
-
-/* sexy colours and font for the header */
-.headerForeground {
-	color: [[ColorPalette::PrimaryPale]];
-}
-.headerShadow, .headerShadow a {
-	color: [[ColorPalette::PrimaryMid]];
-}
-.headerForeground, .headerShadow {
-	padding: 1em 1em 0;
-	font-family: 'Trebuchet MS' sans-serif;
-	font-weight:bold;
-}
-.headerForeground .siteSubtitle {
-	color: [[ColorPalette::PrimaryLight]];
-}
-.headerShadow .siteSubtitle {
-	color: [[ColorPalette::PrimaryMid]];
-}
-
-/* make shadow go and down right instead of up and left */
-.headerShadow {
-	left: 2px;
-	top: 3px;
-}
-
-/* prefer monospace for editing */
-.editor textarea {
-	font-family: 'Consolas' monospace;
-}
-
-/* sexy tiddler titles */
-.title {
-	font-size: 250%;
-	color: [[ColorPalette::PrimaryLight]];
-	font-family: 'Trebuchet MS' sans-serif;
-}
-
-/* more subtle tiddler subtitle */
-.subtitle {
-	padding:0px;
-	margin:0px;
-	padding-left:0.5em;
-	font-size: 90%;
-	color: [[ColorPalette::TertiaryMid]];
-}
-.subtitle .tiddlyLink {
-	color: [[ColorPalette::TertiaryMid]];
-}
-
-/* a little bit of extra whitespace */
-.viewer {
-	padding-bottom:3px;
-}
-
-/* don't want any background color for headings */
-h1,h2,h3,h4,h5,h6 {
-	background: [[ColorPalette::Background]];
-	color: [[ColorPalette::Foreground]];
-}
-
-/* give tiddlers 3d style border and explicit background */
-.tiddler {
-	background: [[ColorPalette::Background]];
-	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
-	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
-	margin-bottom: 1em;
-	padding-bottom: 2em;
-}
-
-/* make options slider look nicer */
-#sidebarOptions .sliderPanel {
-	border:solid 1px [[ColorPalette::PrimaryLight]];
-}
-
-
-/* the borders look wrong with the body background */
-#sidebar .button {
-	border-style: none;
-}
-
-/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
-.tagglyTagged li.listTitle {
-	display:none
-}
-.tagglyTagged li {
-	display: inline; font-size:90%;
-}
-.tagglyTagged ul {
-	margin:0px; padding:0px;
-}
-
-/* this means you can put line breaks in SidebarOptions for readability */
-#sidebarOptions br {
-	display:none;
-}
-/* undo the above in OptionsPanel */
-#sidebarOptions .sliderPanel br {
-	display:inline;
-}
-
-/* horizontal main menu stuff */
-#displayArea {
-	margin: 1em 15.7em 0em 1em; /* use the freed up space */
-}
-#topMenu br {
-	display: none;
-}
-#topMenu {
-	background: [[ColorPalette::PrimaryMid]];
-	color:[[ColorPalette::PrimaryPale]];
-}
-#topMenu {
-	padding:2px;
-}
-#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
-	margin-left: 0.5em;
-	margin-right: 0.5em;
-	padding-left: 3px;
-	padding-right: 3px;
-	color: [[ColorPalette::PrimaryPale]];
-	font-size: 115%;
-}
-#topMenu .button:hover, #topMenu .tiddlyLink:hover {
-	background: [[ColorPalette::PrimaryDark]];
-}
-
-/* make it print a little cleaner */
-@media print {
-	#topMenu {
-		display: none ! important;
-	}
-	/* not sure if we need all the importants */
-	.tiddler {
-		border-style: none ! important;
-		margin:0px ! important;
-		padding:0px ! important;
-		padding-bottom:2em ! important;
-	}
-	.tagglyTagging .button, .tagglyTagging .hidebutton {
-		display: none ! important;
-	}
-	.headerShadow {
-		visibility: hidden ! important;
-	}
-	.tagglyTagged .quickopentag, .tagged .quickopentag {
-		border-style: none ! important;
-	}
-	.quickopentag a.button, .miniTag {
-		display: none ! important;
-	}
-}
-/*}}}*/
-
-
-
-
The Support Library contains all tools we need at various places, but by themselves don't defines a subsystem on their own.
-
-These things are:
-* [[a Plugin loader|Plugins]]
-* [[ErrorHandling]]
-* a wrapper for POSIX Threads
-** Thread creation joining and canceling
-** [[Locking primitives like Condition variables and Mutexes|LockingPrimitives]]
-* [[Some tools and wrapers around the C library|SafeCLib]]
-* [[O(1) hashtable using Cuckoo hashing|Cuckoo]]
-
-(... to be continued)
-
-
-
-
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
-
- - - - - - - - - - diff --git a/wiki/todo.html b/wiki/todo.html deleted file mode 100644 index 73d716def..000000000 --- a/wiki/todo.html +++ /dev/null @@ -1,11668 +0,0 @@ - - - - - - - - - - - -
My TiddlyWiki is loading ...

Requires Javascript.
- - ToDo - Roadmap and tasks - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #8cf
-PrimaryLight: #18f
-PrimaryMid: #04b
-PrimaryDark: #014
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
/*{{{*/
-body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-
-a {color:[[ColorPalette::PrimaryMid]];}
-a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
-a img {border:0;}
-
-h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
-h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
-h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
-
-.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
-.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
-.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
-
-.header {background:[[ColorPalette::PrimaryMid]];}
-.headerShadow {color:[[ColorPalette::Foreground]];}
-.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
-.headerForeground {color:[[ColorPalette::Background]];}
-.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
-
-.tabSelected{color:[[ColorPalette::PrimaryDark]];
-	background:[[ColorPalette::TertiaryPale]];
-	border-left:1px solid [[ColorPalette::TertiaryLight]];
-	border-top:1px solid [[ColorPalette::TertiaryLight]];
-	border-right:1px solid [[ColorPalette::TertiaryLight]];
-}
-.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
-.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
-.tabContents .button {border:0;}
-
-#sidebar {}
-#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
-#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
-
-.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
-.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
-.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
-	border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
-.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
-.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
-.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
-	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
-.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
-.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
-	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
-
-#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
-#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
-
-.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
-
-.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
-.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
-.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
-.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
-
-.tiddler .defaultCommand {font-weight:bold;}
-
-.shadow .title {color:[[ColorPalette::TertiaryDark]];}
-
-.title {color:[[ColorPalette::SecondaryDark]];}
-.subtitle {color:[[ColorPalette::TertiaryDark]];}
-
-.toolbar {color:[[ColorPalette::PrimaryMid]];}
-.toolbar a {color:[[ColorPalette::TertiaryLight]];}
-.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
-.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
-
-.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
-.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
-.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
-.tagging .button, .tagged .button {border:none;}
-
-.footer {color:[[ColorPalette::TertiaryLight]];}
-.selected .footer {color:[[ColorPalette::TertiaryMid]];}
-
-.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
-.sparktick {background:[[ColorPalette::PrimaryDark]];}
-
-.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
-.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
-.lowlight {background:[[ColorPalette::TertiaryLight]];}
-
-.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
-
-.imageLink, #displayArea .imageLink {background:transparent;}
-
-.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
-
-.viewer .listTitle {list-style-type:none; margin-left:-2em;}
-.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
-.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
-.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
-.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
-.viewer code {color:[[ColorPalette::SecondaryDark]];}
-.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
-
-.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
-
-.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
-.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
-.editorFooter {color:[[ColorPalette::TertiaryMid]];}
-
-#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
-#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
-#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
-#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
-.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
-.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
-#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
-/*}}}*/
-
-
-
/*{{{*/
-* html .tiddler {height:1%;}
-
-body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
-
-h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
-h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
-h4,h5,h6 {margin-top:1em;}
-h1 {font-size:1.35em;}
-h2 {font-size:1.25em;}
-h3 {font-size:1.1em;}
-h4 {font-size:1em;}
-h5 {font-size:.9em;}
-
-hr {height:1px;}
-
-a {text-decoration:none;}
-
-dt {font-weight:bold;}
-
-ol {list-style-type:decimal;}
-ol ol {list-style-type:lower-alpha;}
-ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol {list-style-type:decimal;}
-ol ol ol ol ol {list-style-type:lower-alpha;}
-ol ol ol ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol ol ol ol {list-style-type:decimal;}
-
-.txtOptionInput {width:11em;}
-
-#contentWrapper .chkOptionInput {border:0;}
-
-.externalLink {text-decoration:underline;}
-
-.indent {margin-left:3em;}
-.outdent {margin-left:3em; text-indent:-3em;}
-code.escaped {white-space:nowrap;}
-
-.tiddlyLinkExisting {font-weight:bold;}
-.tiddlyLinkNonExisting {font-style:italic;}
-
-/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
-a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
-
-#mainMenu .tiddlyLinkExisting,
-	#mainMenu .tiddlyLinkNonExisting,
-	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
-#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
-
-.header {position:relative;}
-.header a:hover {background:transparent;}
-.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
-.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
-
-.siteTitle {font-size:3em;}
-.siteSubtitle {font-size:1.2em;}
-
-#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
-
-#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
-#sidebarOptions {padding-top:0.3em;}
-#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
-#sidebarOptions input {margin:0.4em 0.5em;}
-#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
-#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
-#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
-#sidebarTabs .tabContents {width:15em; overflow:hidden;}
-
-.wizard {padding:0.1em 1em 0em 2em;}
-.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizardStep {padding:1em 1em 1em 1em;}
-.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
-.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
-.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
-.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
-
-#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
-.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
-#messageArea a {text-decoration:underline;}
-
-.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
-.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
-
-.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
-.popup .popupMessage {padding:0.4em;}
-.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
-.popup li.disabled {padding:0.4em;}
-.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
-.listBreak {font-size:1px; line-height:1px;}
-.listBreak div {margin:2px 0;}
-
-.tabset {padding:1em 0em 0em 0.5em;}
-.tab {margin:0em 0em 0em 0.25em; padding:2px;}
-.tabContents {padding:0.5em;}
-.tabContents ul, .tabContents ol {margin:0; padding:0;}
-.txtMainTab .tabContents li {list-style:none;}
-.tabContents li.listLink { margin-left:.75em;}
-
-#contentWrapper {display:block;}
-#splashScreen {display:none;}
-
-#displayArea {margin:1em 17em 0em 14em;}
-
-.toolbar {text-align:right; font-size:.9em;}
-
-.tiddler {padding:1em 1em 0em 1em;}
-
-.missing .viewer,.missing .title {font-style:italic;}
-
-.title {font-size:1.6em; font-weight:bold;}
-
-.missing .subtitle {display:none;}
-.subtitle {font-size:1.1em;}
-
-.tiddler .button {padding:0.2em 0.4em;}
-
-.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
-.isTag .tagging {display:block;}
-.tagged {margin:0.5em; float:right;}
-.tagging, .tagged {font-size:0.9em; padding:0.25em;}
-.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
-.tagClear {clear:both;}
-
-.footer {font-size:.9em;}
-.footer li {display:inline;}
-
-.annotation {padding:0.5em; margin:0.5em;}
-
-* html .viewer pre {width:99%; padding:0 0 1em 0;}
-.viewer {line-height:1.4em; padding-top:0.5em;}
-.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
-.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
-.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
-
-.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
-.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
-table.listView {font-size:0.85em; margin:0.8em 1.0em;}
-table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
-
-.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
-.viewer code {font-size:1.2em; line-height:1.4em;}
-
-.editor {font-size:1.1em;}
-.editor input, .editor textarea {display:block; width:100%; font:inherit;}
-.editorFooter {padding:0.25em 0em; font-size:.9em;}
-.editorFooter .button {padding-top:0px; padding-bottom:0px;}
-
-.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
-
-.sparkline {line-height:1em;}
-.sparktick {outline:0;}
-
-.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
-.zoomer div {padding:1em;}
-
-* html #backstage {width:99%;}
-* html #backstageArea {width:99%;}
-#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageToolbar {position:relative;}
-#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
-#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
-#backstage {position:relative; width:100%; z-index:50;}
-#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
-.backstagePanelFooter {padding-top:0.2em; float:right;}
-.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
-#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
-
-.whenBackstage {display:none;}
-.backstageVisible .whenBackstage {display:block;}
-/*}}}*/
-
-
-
/***
-StyleSheet for use when a translation requires any css style changes.
-This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
-***/
-
-/*{{{*/
-body {font-size:0.8em;}
-
-#sidebarOptions {font-size:1.05em;}
-#sidebarOptions a {font-style:normal;}
-#sidebarOptions .sliderPanel {font-size:0.95em;}
-
-.subtitle {font-size:0.8em;}
-
-.viewer table.listView {font-size:0.95em;}
-
-.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
-/*}}}*/
-
-
-
/*{{{*/
-@media print {
-#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
-#displayArea {margin: 1em 1em 0em 1em;}
-/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
-noscript {display:none;}
-}
-/*}}}*/
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-<div class='headerShadow'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-<div class='headerForeground'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-</div>
-<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
-<div id='sidebar'>
-<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-<div id='messageArea'></div>
-<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
-<div class='title' macro='view title'></div>
-<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
-<div class='tagging' macro='tagging'></div>
-<div class='tagged' macro='tags'></div>
-<div class='viewer' macro='view text wikified'></div>
-<div class='tagClear'></div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
-<div class='title' macro='view title'></div>
-<div class='editor' macro='edit title'></div>
-<div macro='annotations'></div>
-<div class='editor' macro='edit text'></div>
-<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
-<!--}}}-->
-
-
-
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
-* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
-* MainMenu: The menu (usually on the left)
-* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
-You'll also need to enter your username for signing your edits: <<option txtUserName>>
-
-
-
These InterfaceOptions for customising TiddlyWiki are saved in your browser
-
-Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
-
-<<option txtUserName>>
-<<option chkSaveBackups>> SaveBackups
-<<option chkAutoSave>> AutoSave
-<<option chkRegExpSearch>> RegExpSearch
-<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
-<<option chkAnimate>> EnableAnimations
-
-----
-Also see AdvancedOptions
-
-
- -
-
-
A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far.  Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:
-<<<
-<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.
-
-Try modifying the time spent.  Suppose you've just spent one more hour and want to record it.  Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window.  Watch the time remaining go down to 1 hour.
-
-In reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours.  The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:
-<<<
-<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-You can adjust the current estimate in the same way as you adjusted the time spent.  Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".
-
-You can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases).  Click on the time remaining and add an hour by typing "+1".
-
-When the time remaining goes to zero, the task is considered complete:
-<<<
-<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you haven't already done so, try double-clicking the description.  Yes, it really does open up the editor and select just the text of the description.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 0.5>> Put tasksum on the ViewTemplate.
-<<<
-You can specify just the description of a task, and leave it unestimated.  Click the question mark to enter the estimate:
-<<<
-<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-As this task implies, you can enter two values in the popup when you click on any of the time cells.  Separate them with spaces and/or a comma.  Experiment:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-Finally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there.  That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.
-
-And setting up a taskadder is simplicity itself.  Just add {{{<<taskadder>>}}} to your tiddler.  You will see this:
-<<<
-<<taskadder>>
-<<<
-Just type a task description into the first field, and your initial estimate for how long it will take into the second field.  Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler.  Notice that you can just start typing a new task as soon as you're done entering the first one.
-
-You can have as many taskadders as you like in any tiddler.  The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse.  Try adding some tasks here and in the above adder:
-<<<
-<<taskadder>>
-<<<
-Notice that the one you just used takes focus when this tiddler is redisplayed.
-
-A taskadder by default adds tasks above itself.  You can make it add them below by adding a {{{below}}} argument to the macro call:
-<<<
-<<taskadder below>>
-<<<
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
In this tutorial, we've been looking mostly at individual tasks.  In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler.  In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.
-
-To get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them.  Here's an example:
-<<<
-<<tasksum start>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end>>
-<<<
-If you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.
-<<<
-<<tasksum start here>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<tasksum end>>
-<<<
-You can nest these things if you like, just be sure to match starts and ends:
-<<<
-<<tasksum start here>>
-* Time cell manipulation:<<tasksum start>>
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<tasksum end "Cell manipulation:">>
-<<br>>
-* Double-click handling:<<tasksum start>>
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end "Double-clicks:">>
-
-<<tasksum end>>
-<<<
-Finally, the simplest way to use tasksum is to add it to your view template.  See TaskSummaryViewTemplate for an example template.  Note that if no tasks are present between the start and end, nothing is displayed.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort.  However, there are two issues that may affect you.  (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)
-# The task macros don't play nicely with the default TiddlyWiki display of tags.  In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler.  However, this little box may interfere with the tables used by the task macros.  In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible.  In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box.  If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative.  Simply import it into your wiki and rename it to ViewTemplate.  This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>
-# Most view templates also add a minus sign ("-") before the "close" command.  TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out.  But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it.  It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed.  So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.
-
-----
-This ends the tutorial.  To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
-
-
-
PageTemplate
-|>|SiteTitle - SiteSubtitle|
-|>|MainMenu|
-|DefaultTiddlers<<br>><<br>><<br>>ViewTemplate<<br>><<br>>EditTemplate|SideBarOptions|
-|~|OptionsPanel|
-|~|SideBarTabs|
-|~|AdvancedOptions|
-|~|<<tiddler Configuration.SideBarTabs>>|
-
-''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
-
-ColorPalette
-
-SiteUrl
-
-
-
/***
-|Name|BetterTimelineMacro|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#BetterTimelineMacro|
-|Version|0.5 beta|
-|Requires|~TW2.x|
-!!!Description:
-A replacement for the core timeline macro that offers more features:
-*list tiddlers with only specfic tag
-*exclude tiddlers with a particular tag
-*limit entries to any number of days, for example one week
-*specify a start date for the timeline, only tiddlers after that date will be listed.
-
-!!!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!!!Syntax:
-{{{<<timeline better:true>>}}}
-''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
-
-additonal params:
-(use only the ones you want)
-{{{<<timeline better:true  onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30>>}}}
-
-''explanation of syntax:''
-onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
-excludeTag: tiddlers with this tag will not be listed.
-sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
-firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
-maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
-maxEntries: limit the total number of entries in the timeline.
-
-
-!!!History:
-*28-07-06: ver 0.5 beta, first release
-
-!!!Code
-***/
-//{{{
-// Return the tiddlers as a sorted array
-TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
-{
-          var results = [];
-          this.forEachTiddler(function(title,tiddler)
-          {
-          if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
-                        if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
-                                      results.push(tiddler);
-          });
-          if(field)
-                   results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
-          return results;
-}
-
-
-
-//this function by Udo
-function getParam(params, name, defaultValue)
-{
-          if (!params)
-          return defaultValue;
-          var p = params[0][name];
-          return p ? p[0] : defaultValue;
-}
-
-window.old_timeline_handler= config.macros.timeline.handler;
-config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
-{
-          var args = paramString.parseParams("list",null,true);
-          var betterMode = getParam(args, "better", "false");
-          if (betterMode == 'true')
-          {
-          var sortBy = getParam(args,"sortBy","modified");
-          var excludeTag = getParam(args,"excludeTag",undefined);
-          var includeTag = getParam(args,"onlyTag",undefined);
-          var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
-          var firstDayParam = getParam(args,"firstDay",undefined);
-          var firstDay = (firstDayParam!=undefined)? firstDayParam: "00010101";
-          var lastDay = "";
-          var field= sortBy;
-          var maxDaysParam = getParam(args,"maxDays",undefined);
-          var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
-          var maxEntries = getParam(args,"maxEntries",undefined);
-          var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
-          for(var t=tiddlers.length-1; t>=last; t--)
-                  {
-                  var tiddler = tiddlers[t];
-                  var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
-                  if ((theDay>=firstDay)&& (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
-                     {
-                     if(theDay != lastDay)
-                               {
-                               var theDateList = document.createElement("ul");
-                               place.appendChild(theDateList);
-                               createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
-                               lastDay = theDay;
-                               }
-                  var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
-                  theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
-                  }
-                  }
-          }
-
-          else
-              {
-              window.old_timeline_handler.apply(this,arguments);
-              }
-}
-//}}}
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #aaa
-PrimaryLight: #f00
-PrimaryMid: #400
-PrimaryDark: #000
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
[[Overview]]
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
-
/***
-|''Name:''|InlineJavascriptPlugin|
-|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
-|''Author:''|Eric Shulman - ELS Design Studios|
-|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
-|''~CoreVersion:''|2.0.10|
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Display script source in tiddler output''
-By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-><script show>
- alert('InlineJavascriptPlugin: this is a demonstration message');
-</script>
-dynamic output:
-><script show>
- return (new Date()).toString();
-</script>
-wikified dynamic output:
-><script show>
- return "link to current user: [["+config.options.txtUserName+"]]";
-</script>
-dynamic output using 'place' to get size information for current tiddler:
-><script show>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-creating an 'onclick' button/link that runs a script:
-><script label="click here" show>
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-loading a script from a source url:
->http://www.TiddlyTools.com/demo.js contains:
->>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
->>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
-><script src="demo.js" show>
- return "loading demo.js..."
-</script>
-><script label="click to execute demo() function" show>
- demo()
-</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
-''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
-''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]'' initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[4]) { // there is script code
- if (lookaheadMatch[3]) // show inline script code in tiddler output
- wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
- if (lookaheadMatch[2]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[4]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else { // run inline script code
- var code="function _out(place){"+lookaheadMatch[4]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
- }
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
''[[Lumiera|index.html]]''
-RoadMap
-ToDo
-[[Tasks]]
-[[Admin]]
-<<fullscreen>>
-
-
-
<!--{{{-->
-<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
-<!--}}}-->
-
-<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>My TiddlyWiki</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>
-
-
-
Here we describe the things which are planned and going on.
-
-There is:
- * A RoadMap which gives a coarse overview of the project.
- * A ToDo list, showing whats left to be done on a intermediate to high level
- * A list of detailed [[Tasks]] which are imminent to do.
-
-The goal of this wiki is not to track each and every task, but show whats going on and where possible contributors can pick a job.
-
-
-
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-	<div class='headerShadow'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-	<div class='headerForeground'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-</div>
-<!-- horizontal MainMenu -->
-<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
-<!-- original MainMenu menu -->
-<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
-<div id='sidebar'>
-	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-	<div id='messageArea'></div>
-	<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
-
/***
-|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
-|''Version:''|1.0.6 (2006-11-07)|
-|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
-|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
-|''Licence:''|[[BSD open source license]]|
-|''TiddlyWiki:''|2.0|
-|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
-!Table of Content<html><a name="TOC"/></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
-!Description<html><a name="Description"/></html>
-With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
-Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts, use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
-
-''Syntax:'' 
-|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
-|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//.|
-|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
-|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
-|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Applications<html><a name="Applications"/></html>
-!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
-Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
-
-Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Citation Index<html><a name="Citation"/></html>
-Create a tiddler "Citations" that contains your "citations". 
-Wrap every citation with a part and a proper name. 
-
-''Example''
-{{{
-<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
-in //Proc. ICSM//, 1998.</part>
-
-<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
-Thesis, Uni Stuttgart, 2002.</part>
-
-<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
-in //Proc. ICSM//, 1999.</part>
-}}}
-
-You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
-You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
-{{{
-* Item 1
-* Item 2
-* Item 3
-}}}
-into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
-
-Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
-
-''Example''
-{{{
-|!Subject|!Items|
-|subject1|<<tiddler ./Cell1>>|
-|subject2|<<tiddler ./Cell2>>|
-
-<part Cell1 hidden>
-* Item 1
-* Item 2
-* Item 3
-</part>
-...
-}}}
-
-Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
-
-BTW: The same approach can be used to create bullet lists with items that contain more than one line.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating Tabs<html><a name="Tabs"/></html>
-The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
-
-With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
-
-''Example''
-The standard tabs at the sidebar are defined by the following eight tiddlers:
-* SideBarTabs
-* TabAll
-* TabMore
-* TabMoreMissing
-* TabMoreOrphans
-* TabMoreShadowed
-* TabTags
-* TabTimeline
-
-Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
-{{{
-<<tabs txtMainTab 
- Timeline Timeline SideBarTabs/Timeline 
- All 'All tiddlers' SideBarTabs/All 
- Tags 'All tags' SideBarTabs/Tags 
- More 'More lists' SideBarTabs/More>>
-<part Timeline hidden><<timeline>></part>
-<part All hidden><<list all>></part>
-<part Tags hidden><<allTags>></part>
-<part More hidden><<tabs txtMoreTab 
- Missing 'Missing tiddlers' SideBarTabs/Missing 
- Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
- Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
-<part Missing hidden><<list missing>></part>
-<part Orphans hidden><<list orphans>></part>
-<part Shadowed hidden><<list shadowed>></part>
-}}}
-
-Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
-
-E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
-{{{
-<<forEachTiddler 
- sortBy 'tiddler.modified' descending 
- write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
-}}}
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Using Sliders<html><a name="Sliders"/></html>
-Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
-
-''Example''
-In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
-{{{
-...
-<<slider chkAboutDetails About/Details details "Click here to see more details">>
-<part Details hidden>
-To give you a better overview ...
-</part>
-...
-}}}
-
-Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Revision history<html><a name="Revisions"/></html>
-* v1.0.6 (2006-11-07)
-** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
-* v1.0.5 (2006-03-02)
-** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
-* v1.0.4 (2006-02-28)
-** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
-* v1.0.3 (2006-02-26)
-** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
-* v1.0.2 (2006-02-05)
-** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
-* v1.0.1 (2006-01-27)
-** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
-** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
-* v1.0.0 (2006-01-25)
-** initial version
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Code<html><a name="Code"/></html>
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-//{{{
-//============================================================================
-// PartTiddlerPlugin
-
-// Ensure that the PartTiddler Plugin is only installed once.
-//
-if (!version.extensions.PartTiddlerPlugin) {
-
-
-
-version.extensions.PartTiddlerPlugin = {
- major: 1, minor: 0, revision: 6,
- date: new Date(2006, 10, 7), 
- type: 'plugin',
- source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
-};
-
-if (!window.abego) window.abego = {};
-if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
-
-//============================================================================
-// Common Helpers
-
-// Looks for the next newline, starting at the index-th char of text. 
-//
-// If there are only whitespaces between index and the newline 
-// the index behind the newline is returned, 
-// otherwise (or when no newline is found) index is returned.
-//
-var skipEmptyEndOfLine = function(text, index) {
- var re = /(\n|[^\s])/g;
- re.lastIndex = index;
- var result = re.exec(text);
- return (result && text.charAt(result.index) == '\n') 
- ? result.index+1
- : index;
-}
-
-
-//============================================================================
-// Constants
-
-var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
-var partEndTagREString = "<\\/part>";
-var partEndTagString = "</part>";
-
-//============================================================================
-// Plugin Specific Helpers
-
-// Parse the parameters inside a <part ...> tag and return the result.
-//
-// @return [may be null] {partName: ..., isHidden: ...}
-//
-var parseStartTagParams = function(paramText) {
- var params = paramText.readMacroParams();
- if (params.length == 0 || params[0].length == 0) return null;
- 
- var name = params[0];
- var paramsIndex = 1;
- var hidden = false;
- if (paramsIndex < params.length) {
- hidden = params[paramsIndex] == "hidden";
- paramsIndex++;
- }
- 
- return {
- partName: name, 
- isHidden: hidden
- };
-}
-
-// Returns the match to the next (end or start) part tag in the text, 
-// starting the search at startIndex.
-// 
-// When no such tag is found null is returned, otherwise a "Match" is returned:
-// [0]: full match
-// [1]: matched "end" tag (or null when no end tag match)
-// [2]: matched "start" tag (or null when no start tag match)
-// [3]: content of start tag (or null if no start tag match)
-//
-var findNextPartEndOrStartTagMatch = function(text, startIndex) {
- var re = new RegExp(partEndOrStartTagRE);
- re.lastIndex = startIndex;
- var match = re.exec(text);
- return match;
-}
-
-//============================================================================
-// Formatter
-
-// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
-//
-// @return true if a complete part section (including the end tag) could be processed, false otherwise.
-//
-var handlePartSection = function(w) {
- var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
- if (!tagMatch) return false;
- if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
-
- // Parse the start tag parameters
- var arguments = parseStartTagParams(tagMatch[3]);
- if (!arguments) return false;
- 
- // Continue processing
- var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
- var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
- if (endMatch && endMatch[1]) {
- if (!arguments.isHidden) {
- w.nextMatch = startTagEndIndex;
- w.subWikify(w.output,partEndTagREString);
- }
- w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
- 
- return true;
- }
- return false;
-}
-
-config.formatters.push( {
- name: "part",
- match: "<part\\s+[^>]+>",
- 
- handler: function(w) {
- if (!handlePartSection(w)) {
- w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
- }
- }
-} )
-
-//============================================================================
-// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
-// as tiddlers.
-
-var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
-
-// Return the match to the first <part ...> tag of the text that has the
-// requrest partName.
-//
-// @return [may be null]
-//
-var findPartStartTagByName = function(text, partName) {
- var i = 0;
- 
- while (true) {
- var tagMatch = findNextPartEndOrStartTagMatch(text, i);
- if (!tagMatch) return null;
-
- if (tagMatch[2]) {
- // Is start tag
- 
- // Check the name
- var arguments = parseStartTagParams(tagMatch[3]);
- if (arguments && arguments.partName == partName) {
- return tagMatch;
- }
- }
- i += tagMatch[0].length;
- }
-}
-
-// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
-// object, using fullName as the Tiddler's title. 
-//
-// All remaining properties of the new Tiddler (tags etc.) are inherited from 
-// the parentTiddler.
-// 
-// @return [may be null]
-//
-var getPart = function(parentTiddler, partName, fullName) {
- var text = parentTiddler.text;
- var startTag = findPartStartTagByName(text, partName);
- if (!startTag) return null;
- 
- var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
- var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
-
- if (indexOfEndTag >= 0) {
- var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
- var partTiddler = new Tiddler();
- partTiddler.set(
- fullName,
- partTiddlerText,
- parentTiddler.modifier,
- parentTiddler.modified,
- parentTiddler.tags,
- parentTiddler.created);
- partTiddler.abegoIsPartTiddler = true;
- return partTiddler;
- }
- 
- return null;
-}
-
-// Hijack the store.fetchTiddler to recognize the "part" addresses.
-//
-
-var oldFetchTiddler = store.fetchTiddler ;
-store.fetchTiddler = function(title) {
- var result = oldFetchTiddler.apply(this, arguments);
- if (!result && title) {
- var i = title.lastIndexOf('/');
- if (i > 0) {
- var parentName = title.substring(0, i);
- var partName = title.substring(i+1);
- var parent = (parentName == ".") 
- ? currentParent 
- : oldFetchTiddler.apply(this, [parentName]);
- if (parent) {
- return getPart(parent, partName, parent.title+"/"+partName);
- }
- }
- }
- return result; 
-};
-
-
-// The user must not edit a readOnly/partTiddler
-//
-
-config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
-
-Tiddler.prototype.isReadOnly = function() {
- // Tiddler.isReadOnly was introduced with TW 2.0.6.
- // For older version we explicitly check the global readOnly flag
- if (config.commands.editTiddler.oldIsReadOnlyFunction) {
- if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
- } else {
- if (readOnly) return true;
- }
-
- return this.abegoIsPartTiddler;
-}
-
-config.commands.editTiddler.handler = function(event,src,title)
-{
- var t = store.getTiddler(title);
- // Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
- // or the tiddler is not readOnly
- if(!t || !t.abegoIsPartTiddler)
- {
- clearMessage();
- story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
- story.focusTiddler(title,"text");
- return false;
- }
-}
-
-// To allow the "./partName" syntax in macros we need to hijack 
-// the invokeMacro to define the "currentParent" while it is running.
-// 
-var oldInvokeMacro = window.invokeMacro;
-function myInvokeMacro(place,macro,params,wikifier,tiddler) {
- var oldCurrentParent = currentParent;
- if (tiddler) currentParent = tiddler;
- try {
- oldInvokeMacro.apply(this, arguments);
- } finally {
- currentParent = oldCurrentParent;
- }
-}
-window.invokeMacro = myInvokeMacro;
-
-// Scroll the anchor anchorName in the viewer of the given tiddler visible.
-// When no tiddler is defined use the tiddler of the target given event is used.
-window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
- var tiddlerElem = null;
- if (tiddler) {
- tiddlerElem = document.getElementById(story.idPrefix + tiddler);
- }
- if (!tiddlerElem && evt) {
- var target = resolveTarget(evt);
- tiddlerElem = story.findContainingTiddler(target);
- }
- if (!tiddlerElem) return;
-
- var children = tiddlerElem.getElementsByTagName("a");
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var name = child.getAttribute("name");
- if (name == anchorName) {
- var y = findPosY(child);
- window.scrollTo(0,y);
- return;
- }
- }
-}
-
-} // of "install only once"
-//}}}
-
-/***
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Licence and Copyright
-Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-Neither the name of abego Software nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
//last update: RSSReaderPlugin v 1.1.1//
-
-!Description
-This plugin provides a RSSReader for TiddlyWiki
-* It accesses asynchronously an RSSFeed
-*Depending on the chanel item format, each item could be written as :
-**simple text wikified
-**html
-
-!Usage
-{{{
-<<rssReader noDesc|asHtml|asText rssUrl ['filtering string']>>
-	noDesc: only title of item is printed
-
-	asHtml: if you know that description contain html (links, img ...), 
-		the text is enclosed with <html> </html> tags
-
- 	asText: if the description should not be interpreted as html the 
-		description is wikified
-
-	rssUrl: the rssFeed url that could be accessed. 
-	
-	'filtering string': if present, the rssfeed item title must contained 
-		this string to be displayed. 
-		If 'filering string' contained space characters only, the tiddler 
-		title is used for filtering.
-
-}}}
-
-For security reasons, if the TiddlyWiki is accessed from http, a ProxyService should be used to access an rssFeed from an other site.
-
-!examples
-| !reader | !RSSFeed type | !working from |
-| BidiXTWRSS | Description asHtml | file: or tiddlywiki.bidix.info |
-| [[Le Monde]] | Description asText | file: or tiddlywiki.bidix.info using proxy |
-| YahooNewsSport | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| TiddlyWikiRSS | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| [[Libération]] | noDesc | file: or tiddlywiki.bidix.info using proxy |
-| [[TestComment]] | asText and filters | file: or tiddlywiki.bidix.info using proxy |
-see : <<tag RSSFeed>> for the full list.
-
-!Revision history
-* V1.1.0 (2207/04/13)
-**No more import functions
-* V1.0.0 (2006/11/11)
-**refactoring using core loadRemoteFile function
-**import using new tiddlywiki:tiddler element
-**import and presentation preserved without EricShulman's NestedSliderPlugin
-**better display of items 
-* v0.3.0 (24/08/2006)
-** Filter on RSS item title
-** Place to display redefined for asynchronous processing
-* v0.2.2 (22/08/2006)
-**Haloscan feed has no pubDate.
-* v0.2.1 (08/05/2006)
-* v0.2.0 (01/05/2006)
-**Small adapations for del.icio.us feed
-* v0.1.1 (28/04/2006)
-**Bug : Channel without title 
-* v0.1.0 (24/04/2006)
-** initial release
-
-
-
-
-
-
! Foundations, not really a milestone yet
-
-We need following in a basically working / mockup state:
-
-* GUI
-** define a outline (most important views and modes)
-** define the foundations for communicating with the lower layers
-* Proc layer
-** basic assets, create a clip object //(done)//
-** interface/stub for ~ConfigQuery subsystem //(done)//
-** add simple clip to EDL/Session, invoke builder
-** build render nodes for very simple session (partly mockups)
-** preliminary support for composite clips (video+audio)
-* Backend
-** File backend, caching
-** Serializer
-** Scheduler
-* Support Libary
-** Plugin Loader
-** Other tools there
-* Infrastructure
-** Testsuite
-** Website
-
-!!! Next Goals
-
-# Make a [[video player mockup]]
-# Integrate a ''Version 0.1'' able to
-#* load a simple media
-#* put a clip to the EDL
-#* build the corresponding low-level model
-#* run a render process
-
-! Second round
-//all rather brainstorming for now, feel free to modify...//
-
-* save and load session
-* integrate YAP Prolog instead of the mock impl
-* load and wire some plugins
-* send output to a window in GUI
-
-
-
-
-
Roadmap and tasks
-
-
-
ToDo
-
-
-
/***
-
-''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
-
-|Name|SplashScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#SplashScreenPlugin|
-|Version|0.21 |
-|Requires|~TW2.08+|
-!Description:
-Provides a simple splash screen that is visible while the TW is loading.
-
-!Installation
-Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
-
-!Customizing
-Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
-
-!History
-* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
-* 26-06-06 : version 0.2, first release
-
-!Code
-***/
-//{{{
-var old_lewcid_splash_restart=restart;
-
-restart = function()
-{   if (document.getElementById("SplashScreen"))
-        document.getElementById("SplashScreen").style.display = "none";
-      if (document.getElementById("contentWrapper"))
-        document.getElementById("contentWrapper").style.display = "block";
-    
-    old_lewcid_splash_restart();
-   
-    if (splashScreenInstall)
-       {if(config.options.chkAutoSave)
-			{saveChanges();}
-        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
-        }
-}
-
-
-var oldText = store.getTiddlerText("MarkupPreHead");
-if (oldText.indexOf("SplashScreen")==-1)
-   {var siteTitle = store.getTiddlerText("SiteTitle");
-   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
-   if (! store.tiddlerExists("MarkupPreHead"))
-       {var myTiddler = store.createTiddler("MarkupPreHead");}
-   else
-      {var myTiddler = store.getTiddler("MarkupPreHead");}
-      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
-      store.setDirty(true);
-      var splashScreenInstall = true;
-}
-//}}}
-
-
-
/*{{{*/
-/* a contrasting background so I can see where one tiddler ends and the other begins */
-body {
-	background: [[ColorPalette::TertiaryLight]];
-}
-
-/* sexy colours and font for the header */
-.headerForeground {
-	color: [[ColorPalette::PrimaryPale]];
-}
-.headerShadow, .headerShadow a {
-	color: [[ColorPalette::PrimaryMid]];
-}
-.headerForeground, .headerShadow {
-	padding: 1em 1em 0;
-	font-family: 'Trebuchet MS' sans-serif;
-	font-weight:bold;
-}
-.headerForeground .siteSubtitle {
-	color: [[ColorPalette::PrimaryLight]];
-}
-.headerShadow .siteSubtitle {
-	color: [[ColorPalette::PrimaryMid]];
-}
-
-/* make shadow go and down right instead of up and left */
-.headerShadow {
-	left: 2px;
-	top: 3px;
-}
-
-/* prefer monospace for editing */
-.editor textarea {
-	font-family: 'Consolas' monospace;
-}
-
-/* sexy tiddler titles */
-.title {
-	font-size: 250%;
-	color: [[ColorPalette::PrimaryLight]];
-	font-family: 'Trebuchet MS' sans-serif;
-}
-
-/* more subtle tiddler subtitle */
-.subtitle {
-	padding:0px;
-	margin:0px;
-	padding-left:0.5em;
-	font-size: 90%;
-	color: [[ColorPalette::TertiaryMid]];
-}
-.subtitle .tiddlyLink {
-	color: [[ColorPalette::TertiaryMid]];
-}
-
-/* a little bit of extra whitespace */
-.viewer {
-	padding-bottom:3px;
-}
-
-/* don't want any background color for headings */
-h1,h2,h3,h4,h5,h6 {
-	background: [[ColorPalette::Background]];
-	color: [[ColorPalette::Foreground]];
-}
-
-/* give tiddlers 3d style border and explicit background */
-.tiddler {
-	background: [[ColorPalette::Background]];
-	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
-	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
-	margin-bottom: 1em;
-	padding-bottom: 2em;
-}
-
-/* make options slider look nicer */
-#sidebarOptions .sliderPanel {
-	border:solid 1px [[ColorPalette::PrimaryLight]];
-}
-
-
-/* the borders look wrong with the body background */
-#sidebar .button {
-	border-style: none;
-}
-
-/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
-.tagglyTagged li.listTitle {
-	display:none
-}
-.tagglyTagged li {
-	display: inline; font-size:90%;
-}
-.tagglyTagged ul {
-	margin:0px; padding:0px;
-}
-
-/* this means you can put line breaks in SidebarOptions for readability */
-#sidebarOptions br {
-	display:none;
-}
-/* undo the above in OptionsPanel */
-#sidebarOptions .sliderPanel br {
-	display:inline;
-}
-
-/* horizontal main menu stuff */
-#displayArea {
-	margin: 1em 15.7em 0em 1em; /* use the freed up space */
-}
-#topMenu br {
-	display: none;
-}
-#topMenu {
-	background: [[ColorPalette::PrimaryMid]];
-	color:[[ColorPalette::PrimaryPale]];
-}
-#topMenu {
-	padding:2px;
-}
-#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
-	margin-left: 0.5em;
-	margin-right: 0.5em;
-	padding-left: 3px;
-	padding-right: 3px;
-	color: [[ColorPalette::PrimaryPale]];
-	font-size: 115%;
-}
-#topMenu .button:hover, #topMenu .tiddlyLink:hover {
-	background: [[ColorPalette::PrimaryDark]];
-}
-
-/* make it print a little cleaner */
-@media print {
-	#topMenu {
-		display: none ! important;
-	}
-	/* not sure if we need all the importants */
-	.tiddler {
-		border-style: none ! important;
-		margin:0px ! important;
-		padding:0px ! important;
-		padding-bottom:2em ! important;
-	}
-	.tagglyTagging .button, .tagglyTagging .hidebutton {
-		display: none ! important;
-	}
-	.headerShadow {
-		visibility: hidden ! important;
-	}
-	.tagglyTagged .quickopentag, .tagged .quickopentag {
-		border-style: none ! important;
-	}
-	.quickopentag a.button, .miniTag {
-		display: none ! important;
-	}
-}
-/*}}}*/
-
-
-
-
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
-
-
! Things which are imminent to do
-
-<<task 1 1 0.25>> Testsuite: Valgrind suppression file generation, glibc/pthreads leak some memory at the end of the applications, this is ok, we have to integrate this as suppressions into the testsuite, initial work done by cehteh
-<<task >> Testsuite: expected 'out:' and 'err:' should be shell glob or regex lines not exact strings. The output shall be matched against the current 'out:' or 'err:' line, on success the check continues with the actual expected glob, on fail the next glob is tried. When that fails the test is counted as failure.
-<<task >> Testsuite: when no expected 'err:' statements are in a test then stderr, including valgrind reports gets ignored, this is a bug. stderr should be filtered for nobug logging events but everything else should be checked for unexpected output.
-<<task >> Website: asciidoc switchover, git integration, rebuild website on push
-<<taskadder>>
-
-
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
-
-
! Proc layer
- by ichtho
-//the following were migrated to the lumiera-work Mailinglist as of 9/08 //
-* Asset Manager, Asset and ~MObject
-* Support for Media Bins, high-level Resource manager
-* Session, EDL, edit operations
-* Framework for storing/loading objects
-* Defaults Manager ~ConfigQuery subsystem
-* integrating YAP Prolog or another resolution system
-* create Fixture from all ~EDLs
-* create Partitioning of Fixture
-* Automation handling
-
-
-!! Builder core
-support framework done. Design/planning of node creater tool partially done, implementation just started
-!! Render nodes
-Interface and outline of the "operaton protocol" is done as of 8/08. Details of the implementation in work.
-
-! Backend
- by cehteh
-!! File handling
-Opening and accessing huge files for reading and writing
-!!! Manageing File opening, desciptors and handles
-In progress
-!!! Mmaping backend, caching
-Design finished, nothing implemented yet.
-!! Serializer
-Some ideas, nothing done yet.
-!! Scheduler
-Design ideas, nothing done yet.
-
-! Support Libary
-by cehteh and ichthyo
-
-Ongoing efforts as needed, some things already deprecated ;)
-!! Plugin Loader
-Basic implementation finished, not final yet.
-
-! Infrastructure
-!! Testsuite
-Needs some improvements, see [[Tasks]]
-!! Website
-A mockup website is online, raffa experimenting with asciidoc which looks good so far. See [[Tasks]]
-!! git / mob repos
-gitweb is up, git repos are established, mob repos are not yet up.
-!! automatic generation and checkout of tiddlywikis and doxygen docs on the server
-currently only a manual rsync script in admin/
-!! automatic builds/test on server, status page
-will be done on the 'devel' vserver. nothing done yet.
-
-
-
-
Use the backend, gmerlin/avdecoder and a Player widget to playback videos. This should be a small application which can open and playback many videos from on instance in parallel. Later on scrubbing for this videos might be supported.
-
-The idea here is to show and measure backend and scheduler performance and beeing a proof of concept for the design. It will run independent of the Proc layer but needs quite some work on the backend to be done.
-
-
- - - - - - - - - - From f7a9414084afe9570f2fade9180e8f34f080bb09 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 11 Jan 2012 06:56:37 +0100 Subject: [PATCH 278/296] Documentation of the SCons buildsystem --- doc/technical/build/SCons.txt | 191 ++++++++++++++++++++++++++++++++++ doc/technical/build/index.txt | 3 + 2 files changed, 194 insertions(+) create mode 100644 doc/technical/build/SCons.txt diff --git a/doc/technical/build/SCons.txt b/doc/technical/build/SCons.txt new file mode 100644 index 000000000..46b8647a3 --- /dev/null +++ b/doc/technical/build/SCons.txt @@ -0,0 +1,191 @@ +SCons Build-System +================== +:author: Ichthyo +:date: 2012 + +//MENU: label SCons Build + +Lumiera uses a build system based on link:http://scons.org[SCons] + +SCons is an open source software construction tool based on Python build definition scripts. +Within these build scripts, we define a data structure to describe the parts and dependencies +of our software. When executed, SCons evaluates those definitions and the actual files in +the source tree to derive a build strategy, which is then performed. + +SCons core concepts +------------------- +^_this section is based on the introductory pages on the link:http://www.scons.org/wiki/BasicConcepts[SCons Wiki]_^ + +.SCons Environment +When SCons starts building the project, it creates its own environment with dependency trees, +helper functions, builders and other stuff. The SCons environment is created in memory and some parts of it +are saved to disk to speed up things on the next start. The definition of the build happens within this +artificial build environment. This often confuses people who used Makefiles, where environment is actually +the System Environment. + +.System Environment +the familiar operating system container with environment variables such as PATH, HOME etc. +It is usually accessible via os.environ mapping in Python and therefore in SCons too. +SCons doesn't import any settings from System Environment automatically (like flags for compilers, +or paths for tools), because it's designed to be a cross platform tool with _predictable behaviour._ +That's why, if you rely on any system PATHs or environment variables -- you need to extract +those settings explicitly in your build definition. + +.SConstruct +when SCons executes, it performs a build definition python script written by the user. +By convention, this main script is called 'SConstruct' and is located in the root of the source tree. +It is a full featured Python module executed within a specifically prepared environment. + +.SConscript +these files are also SCons scripts, but they are placed in subdirectories of the project. +Typically they are used to organize hierarchical builds and are included from the main SConstruct file +from the project root. Often, all of the actual build definitions reside in SConscript files in +the sub-trees of the project. + +.Builder +The SCons buildsystem revolves around the metaphor of a _Builder_. This is a SCons object that you explicitly +invoke from the scripts to _define_ that there is something to build, transforming a _source_ into a _target_. +So the target depends on the sources, and typically those _source nodes_ were created by previous builder invocations. +The power of SCons is in the fact that dependencies are tracked automatically. When source files change, the system +is able to derive which targets to rebuild. + +.Scanner +when defining a builder, SCons relies on modular scanner components to ``understand'' the source of the build step. +They may scan source files to discover additional dependencies referenced inside. Thus, SCons comes with built-in +knowledge about the source files and artefacts to be created by a typical build, and further types can be added +through plug-ins. + +.Tool +any further, external component that adds Builders, Scanners and other helpers to SCons environments +for use within scripts. There are special tools for _configuring the platform_ to detect libraries and +further requirements. Relying on those tools. the build environment will be outfitted to reflect the +needs of the specific build. Sub-environments with special configuration may be created. + +.Target +any _node_ or ``build step'' encountered through the definition of the build is a _target_. The actual build +will be triggered by requesting a target, which typically might be just an executable known to reside at some +location in the tree. Special _alias targets_ may be defined, based on other targets, to trigger off standard +build situations. Especially, a _default_ target may be defined. + +'''' + +Organisation of the Lumiera SCons build +--------------------------------------- +Within our build system, we exploit the power of the Python programming language to create abstractions +tailored to the needs ouf our project. Located in the `admin/scons` subdirectory, you'll find a collection +of Python modules defining these building blocks. + +- the *LumieraEnvironment* is created as a subclass of the standard SCons build environment; it is + outfitted with pre-configured custom builders for executables, libraries, extension module, Lumiera plug-in + and icon resources. +- all these *custom builders* implement a set of conventions and directory locations within the tree. + These are defined (and can be adjusted) in the *Setup.py* module. This way, each builder automatically + places the generated artefacts at standard build and installation locations. +- for defining individual targets and builder invocations, we rely on *build helpers* to process whole + *source sub trees* rather than individual files. Mostly, just placing a source file into the appropriate + sub tree is sufficient to get it compiled, linked and installed in a standard way. + +Sub-trees +~~~~~~~~~ +.the source tree +All sourcecode of the core application resides below `src/`. Building these components is controlled by +the SConscript residing in this application source root. By convention, this is also the root for header +includes -- _all headers should be included relative_ to `src/`. + +.the three layers +Within this application core tree, there are sub-trees for the main layers comprising the application. +Each of these sub-trees will be built into a shared library and then linked against the application framework +and common services residing in `src/common`. Besides, there is a sub-tree for core plug-ins and support tools. + +.the GTK Gui +one of these sub-trees, residing in `src/gui` forms the upper layer or user-interaction layer. Contrary to +the lower layers, the GUI is _optional_ and the application is fully operational _without Gui._ Thus, the +GTK Gui is built and loaded as Lumiera a plug-in. + +.unit tests +Since we're developing test-driven, about half of the overall code can be found in unit- and integration +tests, residing below `test/`. There is a separate SConscript file, defining the various kinds of test +artefacts to be created. + +- plain-C tests are defined in _test-collections_, grouped thematically into several subdirectories. + Here, each translation unit provides a separate +main()+ function and is linked into a stand-alone + executable (yet still linked against the appropriate shared libraries of the main application layers) +- the tests covering C++ components are organised into test-suites, residing in separate sub-trees. + Currently (as of 1/2012), there is the *library suite* and the *proc components suite*. Here + individual translation units define individual test case classes, which are linked together with + a testrunner +main()+ function. + +.research +There is a separate subtree for research and experiments. The rationale being to avoid re-building most +of the core application when it comes to experimenting and trying out new technologies. + +.icons and resources +the +data/+ subtree holds resources, configuration files and icons for the Gui. Most of our icons +are defined as SVG graphics. The build process creates a helper executable (+rsvg_convert+) to render +these vector graphics with the help of lib Cairo into icon collections of various sizes. + +.documentation +Most of the documentation is written in Asciidoc and provided online at link:{ldoc}[the documentation section] +of our website. The plain-text sources of this documentation tree is shipped alongside with the code. +Besides, we build *Doxygen* API documentation there, and we create design and technical specs and drawings +in SVG and in UML. + +.the target directory +This is where the results of the build process are created. Lumiera is organised into a +_self contained folder structure_. As long as the relative locations, as found within +target/+, +are kept intact, the Application will be able to start up and find all its resources. Consequently, +there is no need to ``install'' Lumiera (and the ``install'' target just copies this folder structure +into the standard installation locations of a typical Unix system) + +Unfortunately SCons is a bit wired regarding the object files created during the build process. +So currently, we're just building in-tree. Apologies for that. + + +Invoking the Build +~~~~~~~~~~~~~~~~~~ +All of the build process is launched through the `scons` python script, usually installed into +`/usr/bin` when installing the SCons package onto the system. Just invoking + + scons -h + +prints a summary of all custom options, targets and toggles defined for our build. + +Targets +^^^^^^^ +- *build* is the default target: it creates the shared libs, the application, core plug-ins and the Gui. +- *testcode* additionally builds the research and uint test code +- *check* builds testcode and runs our testsuites +- *research* builds just the research tree +- *doc* builds documentation (currently just Doxygen) +- *all* builds the Application, testsuites and documentation +- *install* builds and installs the Lumiera Application + +By convention, invoking +scons -c+ will _clean up_ everything the given target _would_ build. +Thus, invoking ++scons -c /++ is the most global clean operation: it will clean up al build artefacts and +will un-install Lumiera (recall: every defined node, or directory is also a target). + +Configure checks +^^^^^^^^^^^^^^^^ +SCons doesn't know a separate ``configure'' step. The necessary dependency detection is performed +before each build. Currenntly, we expect _all dependencies to be installed first-class_ into the +system. Please use your package manager. + +Caching and MD5 sums +^^^^^^^^^^^^^^^^^^^^ +SCons stores MD5 sums of all source files, all configure checks and all the command lines used +to invoke compilers and external tools. The decision, what needs to be rebuilt is based entirely +on these checksums. For one, this means that configure checks are re-run only when necessary. +It also means that changes to some compiler switches will automatically cause all affected parts +of the application to be re-built. And of course it means, that you only ever compile what is +necessary. + +With SCons, there is no need for the usual ``cleaning paranoia''. Similarily, there is no need +for CCache (but using DistCC rocks !). Unfortunatly, calculating those MD5 sums requires some +time on each build, even if the net result is that nothing will happen at all. + +Configuration options +^^^^^^^^^^^^^^^^^^^^^ +We provide several custom configuration options (run +scons -h + to get a summary). All of these +options are *sticky*: once set, the build system will recall them in a file '.optcache' and apply +them the same way in subsequent builds. It is fine to edit '.optcache' with a text editor. + diff --git a/doc/technical/build/index.txt b/doc/technical/build/index.txt index a92d215b4..41a7dd76d 100644 --- a/doc/technical/build/index.txt +++ b/doc/technical/build/index.txt @@ -4,6 +4,9 @@ Lumiera build system As work progresses, we will add more information on the Lumiera build system. //Menu: label Build System +//Menu: prepend child 'Dependencies' +//Menu: prepend child 'SCons' + build -- continuous integration -- packaging From c62eccd25f392deb9e82bb9a8aee46254541dca0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 9 Jan 2012 01:32:24 +0100 Subject: [PATCH 279/296] SCons overhaul/clean-up: disentangle main build The goal is to make the build scrips more clear at first sight. So move the main build targets into a separate SConscript, to make them similar to the tests, research and tools. The final goal of this makeover is to reduce the main SConstruct as much as possible --- SConstruct | 41 ++-------------------------------------- src/SConscript | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 39 deletions(-) create mode 100644 src/SConscript diff --git a/SConstruct b/SConstruct index 16c8c3123..01714c6a9 100644 --- a/SConstruct +++ b/SConstruct @@ -38,7 +38,7 @@ CUSTOPTFILE = 'custom-options' # these are accessible via env.path.xxxx srcIcon = 'icons' -srcConf = 'data/config' +srcConf = '#data/config' buildExe = '#$TARGDIR' buildLib = '#$TARGDIR/modules' buildPlug = '#$TARGDIR/modules' @@ -333,45 +333,8 @@ def defineBuildTargets(env, artifacts): We use a custom function to declare a whole tree of srcfiles. """ - lLib = env.SharedLibrary('lumiera', srcSubtree('src/lib'), install=True) - lApp = env.SharedLibrary('lumieracommon', srcSubtree('src/common'), install=True, LIBS=lLib) - lBack = env.SharedLibrary('lumierabackend', srcSubtree('src/backend'),install=True) - lProc = env.SharedLibrary('lumieraproc', srcSubtree('src/proc'), install=True) - - core = lLib+lApp+lBack+lProc - - artifacts['corelib'] = core - artifacts['support'] = lLib - artifacts['config'] = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) - artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'] + core, install=True) - + artifacts['config'] - ) - - # building Lumiera Plugins - artifacts['plugins'] = [] # currently none - - # render and install Icons - vector_icon_dir = env.path.srcIcon+'svg' - prerendered_icon_dir = env.path.srcIcon+'prerendered' - artifacts['icons'] = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] - + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) - - # the Lumiera GTK GUI - envGtk = env.Clone() - envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm']) - envGtk.Append(LIBS=core) - - guimodule = envGtk.LumieraPlugin('gtk_gui', srcSubtree('src/gui'), install=True) - artifacts['gui'] = ( guimodule - + [env.GuiResource(f) for f in env.Glob('src/gui/*.rc')] - + artifacts['icons'] - ) - # call subdir SConscript(s) for independent components - SConscript(dirs=['src/tool','research','tests'], exports='env artifacts core') + SConscript(dirs=['src','src/tool','research','tests'], exports='env artifacts') diff --git a/src/SConscript b/src/SConscript new file mode 100644 index 000000000..66aa8aa7a --- /dev/null +++ b/src/SConscript @@ -0,0 +1,51 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for the Lumiera Application. +## Definitions how to build the main tree +## + +from Buildhelper import srcSubtree +from Buildhelper import scanSubtree + +Import('env','artifacts') + + +lLib = env.SharedLibrary('lumiera', srcSubtree('lib'), install=True) +lApp = env.SharedLibrary('lumieracommon', srcSubtree('common'), install=True, LIBS=lLib) +lBack = env.SharedLibrary('lumierabackend', srcSubtree('backend'),install=True) +lProc = env.SharedLibrary('lumieraproc', srcSubtree('proc'), install=True) + +core = lLib+lApp+lBack+lProc + +artifacts['corelib'] = core +artifacts['support'] = lLib +artifacts['config'] = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + ) +artifacts['lumiera'] = ( env.Program('lumiera', ['lumiera/main.cpp'] + core, install=True) + + artifacts['config'] + ) + +# building Lumiera Plugins +artifacts['plugins'] = [] # currently none + +# render and install Icons +vector_icon_dir = env.path.srcIcon+'svg' +prerendered_icon_dir = env.path.srcIcon+'prerendered' +artifacts['icons'] = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + +# the Lumiera GTK GUI +envGtk = env.Clone() +envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm']) +envGtk.Append(LIBS=core) + +guimodule = envGtk.LumieraPlugin('gtk_gui', srcSubtree('gui'), install=True) +artifacts['gui'] = ( guimodule + + [env.GuiResource(f) for f in env.Glob('gui/*.rc')] + + artifacts['icons'] + ) + + +Export('core') From 25b21fe575eafddf4620f24cd1816a19838523aa Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 9 Jan 2012 02:27:59 +0100 Subject: [PATCH 280/296] reorder building/installing of Icons no need to define them together with the source. SCons is able to work out the real dependencies just fine. Thus, Icons remain in Main SConstruct --- SConstruct | 15 ++++++++++++++- src/SConscript | 9 --------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/SConstruct b/SConstruct index 01714c6a9..4d015ee0b 100644 --- a/SConstruct +++ b/SConstruct @@ -38,7 +38,7 @@ CUSTOPTFILE = 'custom-options' # these are accessible via env.path.xxxx srcIcon = 'icons' -srcConf = '#data/config' +srcConf = 'data/config' buildExe = '#$TARGDIR' buildLib = '#$TARGDIR/modules' buildPlug = '#$TARGDIR/modules' @@ -333,6 +333,19 @@ def defineBuildTargets(env, artifacts): We use a custom function to declare a whole tree of srcfiles. """ + # define Icons to render and install + vector_icon_dir = env.subst(env.path.srcIcon+'svg') + prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') + print "ICON: vector_icon_dir=%s prerendered=%s" % (vector_icon_dir,prerendered_icon_dir) + artifacts['icons'] = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + + #define Configuration files to install + artifacts['config'] = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + ) + # call subdir SConscript(s) for independent components SConscript(dirs=['src','src/tool','research','tests'], exports='env artifacts') diff --git a/src/SConscript b/src/SConscript index 66aa8aa7a..10cea7404 100644 --- a/src/SConscript +++ b/src/SConscript @@ -19,9 +19,6 @@ core = lLib+lApp+lBack+lProc artifacts['corelib'] = core artifacts['support'] = lLib -artifacts['config'] = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) artifacts['lumiera'] = ( env.Program('lumiera', ['lumiera/main.cpp'] + core, install=True) + artifacts['config'] ) @@ -29,12 +26,6 @@ artifacts['lumiera'] = ( env.Program('lumiera', ['lumiera/main.cpp'] + core, ins # building Lumiera Plugins artifacts['plugins'] = [] # currently none -# render and install Icons -vector_icon_dir = env.path.srcIcon+'svg' -prerendered_icon_dir = env.path.srcIcon+'prerendered' -artifacts['icons'] = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] - + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) # the Lumiera GTK GUI envGtk = env.Clone() From f84da63e111047d101db2e8fedf570aca60fba40 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 9 Jan 2012 02:56:29 +0100 Subject: [PATCH 281/296] use import/export instead of passing an artefacts map --- SConstruct | 52 ++++++++++++++++++++++----------------------- research/SConscript | 3 +-- src/SConscript | 26 +++++++++++------------ src/tool/SConscript | 14 ++++++------ tests/SConscript | 27 +++++++++++------------ 5 files changed, 60 insertions(+), 62 deletions(-) diff --git a/SConstruct b/SConstruct index 4d015ee0b..1170195d5 100644 --- a/SConstruct +++ b/SConstruct @@ -319,7 +319,7 @@ def configurePlatform(env): -def defineSetupTargets(env, artifacts): +def defineSetupTargets(env): """ build operations and targets to be done /before/ compiling. things like creating a source tarball or preparing a version header. """ @@ -327,7 +327,7 @@ def defineSetupTargets(env, artifacts): -def defineBuildTargets(env, artifacts): +def defineBuildTargets(env): """ define the source file/dirs comprising each artifact to be built. setup sub-environments with special build options if necessary. We use a custom function to declare a whole tree of srcfiles. @@ -336,52 +336,51 @@ def defineBuildTargets(env, artifacts): # define Icons to render and install vector_icon_dir = env.subst(env.path.srcIcon+'svg') prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') - print "ICON: vector_icon_dir=%s prerendered=%s" % (vector_icon_dir,prerendered_icon_dir) - artifacts['icons'] = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] - + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) + icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) #define Configuration files to install - artifacts['config'] = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) + config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + ) # call subdir SConscript(s) for independent components - SConscript(dirs=['src','src/tool','research','tests'], exports='env artifacts') + SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') -def definePostBuildTargets(env, artifacts): +def definePostBuildTargets(env): """ define further actions after the core build (e.g. Documentation). define alias targets to trigger the installing. """ - build = env.Alias('build', ( artifacts['lumiera'] - + artifacts['plugins'] - + artifacts['tools'] - + artifacts['gui'] - )) + Import('lumiera plugins tools gui testsuite') + build = env.Alias('build', lumiera + plugins + tools +gui) + # additional files to be cleaned when cleaning 'build' env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) env.Clean ('build', [ 'src/pre.gch' ]) - doxydoc = artifacts['doxydoc'] = env.Doxygen('doc/devel/Doxyfile') + doxydoc = env.Doxygen('doc/devel/Doxyfile') env.Alias ('doc', doxydoc) env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - env.Alias ('all', build+artifacts['testsuite']+doxydoc) + env.Alias ('all', build + testsuite + doxydoc) env.Default('build') # SCons default target -def defineInstallTargets(env, artifacts): +def defineInstallTargets(env): """ define additional artifacts to be installed into target locations. @note: we use customised SCons builders defining install targets for all executables automatically. see LumieraEnvironment.py """ - env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') -# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) + Import('lumiera plugins tools gui testsuite') - env.Alias('install', artifacts['gui']) + env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') +# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) + + env.Alias('install', gui) env.Alias('install', '$DESTDIR') ##################################################################### @@ -397,7 +396,6 @@ env = setupBasicEnvironment(localDefinitions) if not (isCleanupOperation(env) or isHelpRequest()): env = configurePlatform(env) -artifacts = {} # the various things we build. # Each entry actually is a SCons-Node list. # Passing these entries to other builders defines dependencies. @@ -406,8 +404,8 @@ artifacts = {} # 'plugins' : plugin shared lib # 'tools' : small tool applications (e.g mpegtoc) -defineSetupTargets(env, artifacts) -defineBuildTargets(env, artifacts) -definePostBuildTargets(env, artifacts) -defineInstallTargets(env, artifacts) +defineSetupTargets(env) +defineBuildTargets(env) +definePostBuildTargets(env) +defineInstallTargets(env) diff --git a/research/SConscript b/research/SConscript index 0fef0dae8..1d3263a72 100644 --- a/research/SConscript +++ b/research/SConscript @@ -4,9 +4,8 @@ ## Things defined here usuall won't be installed ## -Import('env','artifacts','core') +Import('env core support_lib') -support_lib = artifacts['support'] diff --git a/src/SConscript b/src/SConscript index 10cea7404..c0718205d 100644 --- a/src/SConscript +++ b/src/SConscript @@ -7,7 +7,7 @@ from Buildhelper import srcSubtree from Buildhelper import scanSubtree -Import('env','artifacts') +Import('env icons config') lLib = env.SharedLibrary('lumiera', srcSubtree('lib'), install=True) @@ -15,16 +15,16 @@ lApp = env.SharedLibrary('lumieracommon', srcSubtree('common'), install=True, lBack = env.SharedLibrary('lumierabackend', srcSubtree('backend'),install=True) lProc = env.SharedLibrary('lumieraproc', srcSubtree('proc'), install=True) -core = lLib+lApp+lBack+lProc +core = lLib+lApp+lBack+lProc +core_lib = core +support_lib = lLib -artifacts['corelib'] = core -artifacts['support'] = lLib -artifacts['lumiera'] = ( env.Program('lumiera', ['lumiera/main.cpp'] + core, install=True) - + artifacts['config'] - ) +lumiera = ( env.Program('lumiera', ['lumiera/main.cpp'] + core, install=True) + + config + ) # building Lumiera Plugins -artifacts['plugins'] = [] # currently none +plugins = [] # currently none # the Lumiera GTK GUI @@ -33,10 +33,10 @@ envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm' envGtk.Append(LIBS=core) guimodule = envGtk.LumieraPlugin('gtk_gui', srcSubtree('gui'), install=True) -artifacts['gui'] = ( guimodule - + [env.GuiResource(f) for f in env.Glob('gui/*.rc')] - + artifacts['icons'] - ) +gui = ( guimodule + + icons + + [env.GuiResource(f) for f in env.Glob('gui/*.rc')] + ) -Export('core') +Export('lumiera core core_lib support_lib plugins gui') diff --git a/src/tool/SConscript b/src/tool/SConscript index 49f03addf..85a858d5e 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -3,9 +3,8 @@ ## SConscript - SCons buildscript for tool subdirectory (called by SConstruct) ## -Import('env','artifacts','core') +Import('env core support_lib icons') -support_lib = artifacts['support'] envSvg = env.Clone() envSvg.mergeConf(['librsvg-2.0']) @@ -16,11 +15,12 @@ luidgen = env.Program('luidgen', ['luidgen.c'] + support_lib, install=True) rsvg = envSvg.Program('rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) # build additional test and administrative tools.... -artifacts['tools'] = [ env.Program('hello-world','hello.c', install=True) #### hello world (checks C build) - + luidgen - + rsvg - ] +tools = [ env.Program('hello-world','hello.c', install=True) #### hello world (checks C build) + + luidgen + + rsvg + ] +Export('tools') # Rendering the SVG Icons depends on rsvg-convert -env.Depends(artifacts['icons'], rsvg) +env.Depends(icons, rsvg) diff --git a/tests/SConscript b/tests/SConscript index abbe86d35..12672cd79 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -12,7 +12,7 @@ from Buildhelper import scanSubtree from Buildhelper import globRootdirs from Buildhelper import createPlugins -Import('env','artifacts','core') +Import('env core tools config') env = env.Clone() env.Append(CPPPATH='include') # additional headers for tests @@ -56,27 +56,28 @@ moduledirs = globRootdirs('*') -artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['lib','components'] ] - + [ testCollection(env, dir) for dir in moduledirs if not dir in specials] - + createPlugins(env, 'plugin') - + env.File(glob('*.tests')) # depending on the test definition files for test.sh - + artifacts['config'] - ) +testsuite = ( [ testExecutable(env, dir) for dir in ['lib','components'] ] + + [ testCollection(env, dir) for dir in moduledirs if not dir in specials] + + createPlugins(env, 'plugin') + + env.File(glob('*.tests')) # depending on the test definition files for test.sh + + config + ) +Export('testsuite') # for creating a Valgrind-Suppression file vgsuppr = env.Program('vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms -artifacts['tools'] += [vgsuppr] -Depends(ts,vgsuppr) +tools += [vgsuppr] +Depends(testsuite,vgsuppr) # # actually run the Testsuite # # - the product of running the Testsuite is the ",testlog" -# - it depends on all artifacts defined as "ts" above +# - it depends on all artifacts defined as "testsuite" above # - including the tests/*.tests (suite definition files) # - if not set via options switch, the environment variables # TESTSUITES and VALGRINDFLAGS are explicitly propagated to test.sh @@ -106,7 +107,7 @@ testEnv['ENV']['TEST_CONF'] = env.File("test.conf").abspath testDir = env.Dir('#$TARGDIR') runTest = env.File("test.sh").abspath -runTs = testEnv.Command('#$TARGDIR/,testlog', ts, runTest, chdir=testDir) +runTests = testEnv.Command('#$TARGDIR/,testlog', testsuite, runTest, chdir=testDir) @@ -115,8 +116,8 @@ runTs = testEnv.Command('#$TARGDIR/,testlog', ts, runTest, chdir=testDir) # - 'scons testcode' triggers building of the Testsuite # - 'scons check' triggers building and running # -env.Alias('testcode', ts ) -env.Alias('check', runTs ) +env.Alias('testcode', testsuite ) +env.Alias('check', runTests ) # allow tempfiles of test.sh to be cleaned env.Clean ('check', [',testlog',',testlog.pre',',expect_stdout',',stdout',',stderr',',testtmp','.libs']) From b3c7d90e413cf772d425e11bb89fa152ab57b682 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 9 Jan 2012 04:10:00 +0100 Subject: [PATCH 282/296] Move parts from main SConstruct into dedicated Python modules --- SConstruct | 4 + admin/scons/Options.py | 411 ++++++++++++++++++++++++++++++++++++++++ admin/scons/Platform.py | 411 ++++++++++++++++++++++++++++++++++++++++ admin/scons/Setup.py | 411 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1237 insertions(+) create mode 100644 admin/scons/Options.py create mode 100644 admin/scons/Platform.py create mode 100644 admin/scons/Setup.py diff --git a/SConstruct b/SConstruct index 1170195d5..cd8165eae 100644 --- a/SConstruct +++ b/SConstruct @@ -68,6 +68,10 @@ from Buildhelper import * from LumieraEnvironment import * +import Setup +import Options +import Platform + ##################################################################### def setupBasicEnvironment(localDefinitions): diff --git a/admin/scons/Options.py b/admin/scons/Options.py new file mode 100644 index 000000000..f919995de --- /dev/null +++ b/admin/scons/Options.py @@ -0,0 +1,411 @@ +# -*- python -*- +## +## Options.py - SCons build: command line options and help +## + +# Copyright (C) Lumiera.org +# 2012, 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. +##################################################################### + + +# NOTE: scons -h for help. +# Read more about the SCons build system at: http://www.scons.org +# Basically, this script just /defines/ the components and how they +# fit together. SCons will derive the necessary build steps. + + +#-----------------------------------Configuration +TARGDIR = 'target' +VERSION = '0.pre.01' +TOOLDIR = './admin/scons' # SCons plugins +SCRIPTDIR = './admin' +OPTCACHE = 'optcache' +CUSTOPTFILE = 'custom-options' + +# these are accessible via env.path.xxxx +srcIcon = 'icons' +srcConf = 'data/config' +buildExe = '#$TARGDIR' +buildLib = '#$TARGDIR/modules' +buildPlug = '#$TARGDIR/modules' +buildIcon = '#$TARGDIR/gui/icons' +buildUIRes = '#$TARGDIR/' +buildConf = '#$TARGDIR/config' +installExe = '#$DESTDIR/lib/lumiera' +installLib = '#$DESTDIR/lib/lumiera/modules' +installPlug = '#$DESTDIR/lib/lumiera/modules' +installIcon = '#$DESTDIR/share/lumiera/icons' +installUIRes = '#$DESTDIR/share/lumiera/' +installConf = '#$DESTDIR/lib/lumiera/config' + +#-----------------------------------Configuration +localDefinitions = locals() + + + + +import os +import sys + +sys.path.append(TOOLDIR) +sys.path.append(SCRIPTDIR) + +from Buildhelper import * +from LumieraEnvironment import * + + +##################################################################### + +def setupBasicEnvironment(localDefinitions): + """ define cmdline options, build type decisions + """ + EnsurePythonVersion(2,4) + EnsureSConsVersion(1,0) + + Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 + + vars = defineCmdlineVariables() + env = LumieraEnvironment(variables=vars + ,toolpath = [TOOLDIR] + ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe + ,TARGDIR = TARGDIR + ,DESTDIR = '$INSTALLDIR/$PREFIX' + ,VERSION = VERSION + ) + handleVerboseMessages(env) + + env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root + , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines + , CCFLAGS='-Wall -Wextra ' + , CFLAGS='-std=gnu99' + ) + handleNoBugSwitches(env) + + env.Append(CPPDEFINES = '_GNU_SOURCE') + appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') +# appendCppDefine(env,'OPENGL','USE_OPENGL') + appendVal(env,'ARCHFLAGS','CCFLAGS') # for both C and C++ + appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') + appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') + + # setup search path for Lumiera plugins + appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:ORIGIN/modules\\"' + ,'LUMIERA_PLUGIN_PATH=\\"ORIGIN/modules\\"') + appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' + ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') + + prepareOptionsHelp(vars,env) + vars.Save(OPTCACHE, env) + return env + +def appendCppDefine(env,var,cppVar, elseVal=''): + if env[var]: + env.Append(CPPDEFINES = env.subst(cppVar) ) + elif elseVal: + env.Append(CPPDEFINES = env.subst(elseVal)) + +def appendVal(env,var,targetVar,val=None): + if env[var]: + env.Append( **{targetVar: env.subst(val) or env[var]}) + + +def handleNoBugSwitches(env): + """ set the build level for NoBug. + Release builds imply no DEBUG + whereas ALPHA and BETA require DEBUG + """ + level = env['BUILDLEVEL'] + if level in ['ALPHA', 'BETA']: + if not env['DEBUG']: + print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!' + env.Replace( DEBUG = 1 ) + env.Append(CPPDEFINES = 'EBUG_'+level) + elif level == 'RELEASE': + env.Replace( DEBUG = 0 ) + +def handleVerboseMessages(env): + """ toggle verbose build output """ + if not env['VERBOSE']: + # SetOption('silent', True) + env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" + env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" + env['LINKCOMSTR'] = " Linking --> $TARGET" + env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" + + + + +def defineCmdlineVariables(): + """ several toggles and configuration variables can be set on the commandline, + current settings will be persisted in a options cache file. + you may define custom variable settings in a separate file. + Commandline will override both. + """ + vars = Variables([OPTCACHE, CUSTOPTFILE]) + vars.AddVariables( + ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') + ,('CC', 'Set the C compiler to use.', 'gcc') + ,('CXX', 'Set the C++ compiler to use.', 'g++') + ,PathVariable('CCACHE', 'Integrate with CCache', '', PathVariable.PathAccept) + ,PathVariable('DISTCC', 'Invoke C/C++ compiler commands through DistCC', '', PathVariable.PathAccept) + ,EnumVariable('BUILDLEVEL', 'NoBug build level for debugging', 'ALPHA', allowed_values=('ALPHA', 'BETA', 'RELEASE')) + ,BoolVariable('DEBUG', 'Build with debugging information and no optimisations', False) + ,BoolVariable('OPTIMIZE', 'Build with strong optimisation (-O3)', False) + ,BoolVariable('VALGRIND', 'Run Testsuite under valgrind control', True) + ,BoolVariable('VERBOSE', 'Print full build commands', False) + ,('TESTSUITES', 'Run only Testsuites matching the given pattern', '') +# ,BoolVariable('OPENGL', 'Include support for OpenGL preview rendering', False) +# ,EnumVariable('DIST_TARGET', 'Build target architecture', 'auto', +# allowed_values=('auto', 'i386', 'i686', 'x86_64' ), ignorecase=2) + ,PathVariable('PREFIX', 'Installation dir prefix', 'usr/local', PathVariable.PathAccept) + ,PathVariable('INSTALLDIR', 'Root output directory for install. Final installation will happen in INSTALLDIR/PREFIX/... ', '/', PathVariable.PathIsDir) + ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) + ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) + ) + + return vars + + + +def prepareOptionsHelp(vars,env): + prelude = """ +USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] + Build and optionally install Lumiera. + Without specifying any target, just the (re)build target will run. + Add -c to the commandline to clean up anything a given target would produce + +Special Targets: + build : just compile and link + research: build experimental code (might fail) + testcode: additionally compile the Testsuite + check : build and run the Testsuite + doc : generate documentation (Doxygen) + all : build and testcode and doc + install : install created artifacts at PREFIX + +Configuration Options: +""" + Help(prelude + vars.GenerateHelpText(env)) + + + + +def configurePlatform(env): + """ locate required libs. + setup platform specific options. + Abort build in case of failure. + """ + conf = env.Configure() + # run all configuration checks in the given env + + # Perform checks for prerequisites -------------------------------------------- + problems = [] + if not conf.TryAction('pkg-config --version > $TARGET')[0]: + problems.append('We need pkg-config for including library configurations, exiting.') + + if not conf.CheckLibWithHeader('m', 'math.h','C'): + problems.append('Did not find math.h / libm.') + + if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): + problems.append('Functions for runtime dynamic loading not available.') + + if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): + problems.append('Did not find the pthread lib or pthread.h.') + else: + conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') + conf.env.Append(CCFLAGS = ' -pthread') + + if conf.CheckCHeader('execinfo.h'): + conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') + + if conf.CheckCHeader('valgrind/valgrind.h'): + conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') + else: + print 'Valgrind not found. The use of Valgrind is optional; building without.' + + if not conf.CheckPkgConfig('nobugmt', 201006.1): + problems.append('Did not find NoBug [http://www.lumiera.org/nobug_manual.html].') + else: + conf.env.mergeConf('nobugmt') + + if not conf.CheckCXXHeader('tr1/memory'): + problems.append('We rely on the std::tr1 standard C++ extension for shared_ptr.') + + if not conf.CheckCXXHeader('boost/config.hpp'): + problems.append('We need the C++ boost-libraries.') + else: + if not conf.CheckCXXHeader('boost/scoped_ptr.hpp'): + problems.append('We need boost::scoped_ptr (scoped_ptr.hpp).') + if not conf.CheckCXXHeader('boost/format.hpp'): + problems.append('We need boost::format (header).') + if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): + problems.append('We need boost::program_options (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'): + problems.append('We need the boost::system support library (including binary lib).') + if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): + problems.append('We need the boost::filesystem lib (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): + problems.append('We need the boost regular expression lib (incl. binary lib for linking).') + + + if conf.CheckLib(symbol='clock_gettime'): + print 'Using function clock_gettime() as defined in the C-lib...' + else: + if not conf.CheckLib(symbol='clock_gettime', library='rt'): + problems.append('No library known to provide the clock_gettime() function.') + + if not conf.CheckPkgConfig('gavl', 1.0): + problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') + else: + conf.env.mergeConf('gavl') + + if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): + problems.append('Unable to configure GTK--') + + if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): + problems.append('Unable to configure Lib glib--') + + if not conf.CheckPkgConfig('gthread-2.0', '2.12.4'): + problems.append('Need gthread support lib for glib-- based thread handling.') + + if not conf.CheckPkgConfig('cairomm-1.0', 0.6): + problems.append('Unable to configure Cairo--') + + verGDL = '2.27.1' + if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): + print 'No sufficiently recent (>=%s) version of GDL found. Maybe use custom package gdl-lum?' % verGDL + if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): + problems.append('GNOME Docking Library not found. We either need a sufficiently recent GDL ' + 'version (>=%s), or the custom package "gdl-lum" from Lumiera.org.' % verGDL) + + if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): + problems.append('Need rsvg Library for rendering icons.') + + if not conf.CheckCHeader(['X11/Xutil.h', 'X11/Xlib.h'],'<>'): + problems.append('Xlib.h and Xutil.h required. Please install libx11-dev.') + + if not conf.CheckPkgConfig('xv') : problems.append('Need libXv...') + if not conf.CheckPkgConfig('xext'): problems.append('Need libXext.') + + + # report missing dependencies + if problems: + print "*** unable to build due to the following problems:" + for isue in problems: + print " * %s" % isue + print + print "build aborted." + Exit(1) + + print "** Gathered Library Info: %s" % conf.env.libInfo.keys() + + + # create new env containing the finished configuration + return conf.Finish() + + + +def defineSetupTargets(env): + """ build operations and targets to be done /before/ compiling. + things like creating a source tarball or preparing a version header. + """ + pass ## currently none + + + +def defineBuildTargets(env): + """ define the source file/dirs comprising each artifact to be built. + setup sub-environments with special build options if necessary. + We use a custom function to declare a whole tree of srcfiles. + """ + + # define Icons to render and install + vector_icon_dir = env.subst(env.path.srcIcon+'svg') + prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') + icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + + #define Configuration files to install + config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + ) + + # call subdir SConscript(s) for independent components + SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') + + + +def definePostBuildTargets(env): + """ define further actions after the core build (e.g. Documentation). + define alias targets to trigger the installing. + """ + Import('lumiera plugins tools gui testsuite') + build = env.Alias('build', lumiera + plugins + tools +gui) + + # additional files to be cleaned when cleaning 'build' + env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) + env.Clean ('build', [ 'src/pre.gch' ]) + + doxydoc = env.Doxygen('doc/devel/Doxyfile') + env.Alias ('doc', doxydoc) + env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) + + env.Alias ('all', build + testsuite + doxydoc) + env.Default('build') + # SCons default target + + +def defineInstallTargets(env): + """ define additional artifacts to be installed into target locations. + @note: we use customised SCons builders defining install targets + for all executables automatically. see LumieraEnvironment.py + """ + Import('lumiera plugins tools gui testsuite') + + env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') +# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) + + env.Alias('install', gui) + env.Alias('install', '$DESTDIR') + +##################################################################### + + + + + +### === MAIN === #################################################### + +env = setupBasicEnvironment(localDefinitions) + +if not (isCleanupOperation(env) or isHelpRequest()): + env = configurePlatform(env) + +# the various things we build. +# Each entry actually is a SCons-Node list. +# Passing these entries to other builders defines dependencies. +# 'lumiera' : the App +# 'gui' : the GTK UI (plugin) +# 'plugins' : plugin shared lib +# 'tools' : small tool applications (e.g mpegtoc) + +defineSetupTargets(env) +defineBuildTargets(env) +definePostBuildTargets(env) +defineInstallTargets(env) + diff --git a/admin/scons/Platform.py b/admin/scons/Platform.py new file mode 100644 index 000000000..e8f9d050b --- /dev/null +++ b/admin/scons/Platform.py @@ -0,0 +1,411 @@ +# -*- python -*- +## +## Platform.py - SCons build: platform configuration and library detection +## + +# Copyright (C) Lumiera.org +# 2012, 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. +##################################################################### + + +# NOTE: scons -h for help. +# Read more about the SCons build system at: http://www.scons.org +# Basically, this script just /defines/ the components and how they +# fit together. SCons will derive the necessary build steps. + + +#-----------------------------------Configuration +TARGDIR = 'target' +VERSION = '0.pre.01' +TOOLDIR = './admin/scons' # SCons plugins +SCRIPTDIR = './admin' +OPTCACHE = 'optcache' +CUSTOPTFILE = 'custom-options' + +# these are accessible via env.path.xxxx +srcIcon = 'icons' +srcConf = 'data/config' +buildExe = '#$TARGDIR' +buildLib = '#$TARGDIR/modules' +buildPlug = '#$TARGDIR/modules' +buildIcon = '#$TARGDIR/gui/icons' +buildUIRes = '#$TARGDIR/' +buildConf = '#$TARGDIR/config' +installExe = '#$DESTDIR/lib/lumiera' +installLib = '#$DESTDIR/lib/lumiera/modules' +installPlug = '#$DESTDIR/lib/lumiera/modules' +installIcon = '#$DESTDIR/share/lumiera/icons' +installUIRes = '#$DESTDIR/share/lumiera/' +installConf = '#$DESTDIR/lib/lumiera/config' + +#-----------------------------------Configuration +localDefinitions = locals() + + + + +import os +import sys + +sys.path.append(TOOLDIR) +sys.path.append(SCRIPTDIR) + +from Buildhelper import * +from LumieraEnvironment import * + + +##################################################################### + +def setupBasicEnvironment(localDefinitions): + """ define cmdline options, build type decisions + """ + EnsurePythonVersion(2,4) + EnsureSConsVersion(1,0) + + Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 + + vars = defineCmdlineVariables() + env = LumieraEnvironment(variables=vars + ,toolpath = [TOOLDIR] + ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe + ,TARGDIR = TARGDIR + ,DESTDIR = '$INSTALLDIR/$PREFIX' + ,VERSION = VERSION + ) + handleVerboseMessages(env) + + env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root + , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines + , CCFLAGS='-Wall -Wextra ' + , CFLAGS='-std=gnu99' + ) + handleNoBugSwitches(env) + + env.Append(CPPDEFINES = '_GNU_SOURCE') + appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') +# appendCppDefine(env,'OPENGL','USE_OPENGL') + appendVal(env,'ARCHFLAGS','CCFLAGS') # for both C and C++ + appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') + appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') + + # setup search path for Lumiera plugins + appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:ORIGIN/modules\\"' + ,'LUMIERA_PLUGIN_PATH=\\"ORIGIN/modules\\"') + appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' + ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') + + prepareOptionsHelp(vars,env) + vars.Save(OPTCACHE, env) + return env + +def appendCppDefine(env,var,cppVar, elseVal=''): + if env[var]: + env.Append(CPPDEFINES = env.subst(cppVar) ) + elif elseVal: + env.Append(CPPDEFINES = env.subst(elseVal)) + +def appendVal(env,var,targetVar,val=None): + if env[var]: + env.Append( **{targetVar: env.subst(val) or env[var]}) + + +def handleNoBugSwitches(env): + """ set the build level for NoBug. + Release builds imply no DEBUG + whereas ALPHA and BETA require DEBUG + """ + level = env['BUILDLEVEL'] + if level in ['ALPHA', 'BETA']: + if not env['DEBUG']: + print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!' + env.Replace( DEBUG = 1 ) + env.Append(CPPDEFINES = 'EBUG_'+level) + elif level == 'RELEASE': + env.Replace( DEBUG = 0 ) + +def handleVerboseMessages(env): + """ toggle verbose build output """ + if not env['VERBOSE']: + # SetOption('silent', True) + env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" + env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" + env['LINKCOMSTR'] = " Linking --> $TARGET" + env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" + + + + +def defineCmdlineVariables(): + """ several toggles and configuration variables can be set on the commandline, + current settings will be persisted in a options cache file. + you may define custom variable settings in a separate file. + Commandline will override both. + """ + vars = Variables([OPTCACHE, CUSTOPTFILE]) + vars.AddVariables( + ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') + ,('CC', 'Set the C compiler to use.', 'gcc') + ,('CXX', 'Set the C++ compiler to use.', 'g++') + ,PathVariable('CCACHE', 'Integrate with CCache', '', PathVariable.PathAccept) + ,PathVariable('DISTCC', 'Invoke C/C++ compiler commands through DistCC', '', PathVariable.PathAccept) + ,EnumVariable('BUILDLEVEL', 'NoBug build level for debugging', 'ALPHA', allowed_values=('ALPHA', 'BETA', 'RELEASE')) + ,BoolVariable('DEBUG', 'Build with debugging information and no optimisations', False) + ,BoolVariable('OPTIMIZE', 'Build with strong optimisation (-O3)', False) + ,BoolVariable('VALGRIND', 'Run Testsuite under valgrind control', True) + ,BoolVariable('VERBOSE', 'Print full build commands', False) + ,('TESTSUITES', 'Run only Testsuites matching the given pattern', '') +# ,BoolVariable('OPENGL', 'Include support for OpenGL preview rendering', False) +# ,EnumVariable('DIST_TARGET', 'Build target architecture', 'auto', +# allowed_values=('auto', 'i386', 'i686', 'x86_64' ), ignorecase=2) + ,PathVariable('PREFIX', 'Installation dir prefix', 'usr/local', PathVariable.PathAccept) + ,PathVariable('INSTALLDIR', 'Root output directory for install. Final installation will happen in INSTALLDIR/PREFIX/... ', '/', PathVariable.PathIsDir) + ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) + ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) + ) + + return vars + + + +def prepareOptionsHelp(vars,env): + prelude = """ +USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] + Build and optionally install Lumiera. + Without specifying any target, just the (re)build target will run. + Add -c to the commandline to clean up anything a given target would produce + +Special Targets: + build : just compile and link + research: build experimental code (might fail) + testcode: additionally compile the Testsuite + check : build and run the Testsuite + doc : generate documentation (Doxygen) + all : build and testcode and doc + install : install created artifacts at PREFIX + +Configuration Options: +""" + Help(prelude + vars.GenerateHelpText(env)) + + + + +def configurePlatform(env): + """ locate required libs. + setup platform specific options. + Abort build in case of failure. + """ + conf = env.Configure() + # run all configuration checks in the given env + + # Perform checks for prerequisites -------------------------------------------- + problems = [] + if not conf.TryAction('pkg-config --version > $TARGET')[0]: + problems.append('We need pkg-config for including library configurations, exiting.') + + if not conf.CheckLibWithHeader('m', 'math.h','C'): + problems.append('Did not find math.h / libm.') + + if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): + problems.append('Functions for runtime dynamic loading not available.') + + if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): + problems.append('Did not find the pthread lib or pthread.h.') + else: + conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') + conf.env.Append(CCFLAGS = ' -pthread') + + if conf.CheckCHeader('execinfo.h'): + conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') + + if conf.CheckCHeader('valgrind/valgrind.h'): + conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') + else: + print 'Valgrind not found. The use of Valgrind is optional; building without.' + + if not conf.CheckPkgConfig('nobugmt', 201006.1): + problems.append('Did not find NoBug [http://www.lumiera.org/nobug_manual.html].') + else: + conf.env.mergeConf('nobugmt') + + if not conf.CheckCXXHeader('tr1/memory'): + problems.append('We rely on the std::tr1 standard C++ extension for shared_ptr.') + + if not conf.CheckCXXHeader('boost/config.hpp'): + problems.append('We need the C++ boost-libraries.') + else: + if not conf.CheckCXXHeader('boost/scoped_ptr.hpp'): + problems.append('We need boost::scoped_ptr (scoped_ptr.hpp).') + if not conf.CheckCXXHeader('boost/format.hpp'): + problems.append('We need boost::format (header).') + if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): + problems.append('We need boost::program_options (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'): + problems.append('We need the boost::system support library (including binary lib).') + if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): + problems.append('We need the boost::filesystem lib (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): + problems.append('We need the boost regular expression lib (incl. binary lib for linking).') + + + if conf.CheckLib(symbol='clock_gettime'): + print 'Using function clock_gettime() as defined in the C-lib...' + else: + if not conf.CheckLib(symbol='clock_gettime', library='rt'): + problems.append('No library known to provide the clock_gettime() function.') + + if not conf.CheckPkgConfig('gavl', 1.0): + problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') + else: + conf.env.mergeConf('gavl') + + if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): + problems.append('Unable to configure GTK--') + + if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): + problems.append('Unable to configure Lib glib--') + + if not conf.CheckPkgConfig('gthread-2.0', '2.12.4'): + problems.append('Need gthread support lib for glib-- based thread handling.') + + if not conf.CheckPkgConfig('cairomm-1.0', 0.6): + problems.append('Unable to configure Cairo--') + + verGDL = '2.27.1' + if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): + print 'No sufficiently recent (>=%s) version of GDL found. Maybe use custom package gdl-lum?' % verGDL + if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): + problems.append('GNOME Docking Library not found. We either need a sufficiently recent GDL ' + 'version (>=%s), or the custom package "gdl-lum" from Lumiera.org.' % verGDL) + + if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): + problems.append('Need rsvg Library for rendering icons.') + + if not conf.CheckCHeader(['X11/Xutil.h', 'X11/Xlib.h'],'<>'): + problems.append('Xlib.h and Xutil.h required. Please install libx11-dev.') + + if not conf.CheckPkgConfig('xv') : problems.append('Need libXv...') + if not conf.CheckPkgConfig('xext'): problems.append('Need libXext.') + + + # report missing dependencies + if problems: + print "*** unable to build due to the following problems:" + for isue in problems: + print " * %s" % isue + print + print "build aborted." + Exit(1) + + print "** Gathered Library Info: %s" % conf.env.libInfo.keys() + + + # create new env containing the finished configuration + return conf.Finish() + + + +def defineSetupTargets(env): + """ build operations and targets to be done /before/ compiling. + things like creating a source tarball or preparing a version header. + """ + pass ## currently none + + + +def defineBuildTargets(env): + """ define the source file/dirs comprising each artifact to be built. + setup sub-environments with special build options if necessary. + We use a custom function to declare a whole tree of srcfiles. + """ + + # define Icons to render and install + vector_icon_dir = env.subst(env.path.srcIcon+'svg') + prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') + icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + + #define Configuration files to install + config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + ) + + # call subdir SConscript(s) for independent components + SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') + + + +def definePostBuildTargets(env): + """ define further actions after the core build (e.g. Documentation). + define alias targets to trigger the installing. + """ + Import('lumiera plugins tools gui testsuite') + build = env.Alias('build', lumiera + plugins + tools +gui) + + # additional files to be cleaned when cleaning 'build' + env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) + env.Clean ('build', [ 'src/pre.gch' ]) + + doxydoc = env.Doxygen('doc/devel/Doxyfile') + env.Alias ('doc', doxydoc) + env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) + + env.Alias ('all', build + testsuite + doxydoc) + env.Default('build') + # SCons default target + + +def defineInstallTargets(env): + """ define additional artifacts to be installed into target locations. + @note: we use customised SCons builders defining install targets + for all executables automatically. see LumieraEnvironment.py + """ + Import('lumiera plugins tools gui testsuite') + + env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') +# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) + + env.Alias('install', gui) + env.Alias('install', '$DESTDIR') + +##################################################################### + + + + + +### === MAIN === #################################################### + +env = setupBasicEnvironment(localDefinitions) + +if not (isCleanupOperation(env) or isHelpRequest()): + env = configurePlatform(env) + +# the various things we build. +# Each entry actually is a SCons-Node list. +# Passing these entries to other builders defines dependencies. +# 'lumiera' : the App +# 'gui' : the GTK UI (plugin) +# 'plugins' : plugin shared lib +# 'tools' : small tool applications (e.g mpegtoc) + +defineSetupTargets(env) +defineBuildTargets(env) +definePostBuildTargets(env) +defineInstallTargets(env) + diff --git a/admin/scons/Setup.py b/admin/scons/Setup.py new file mode 100644 index 000000000..d910b04de --- /dev/null +++ b/admin/scons/Setup.py @@ -0,0 +1,411 @@ +# -*- python -*- +## +## Setup.py - SCons build: setup, definitions and compiler flags +## + +# Copyright (C) Lumiera.org +# 2012, 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. +##################################################################### + + +# NOTE: scons -h for help. +# Read more about the SCons build system at: http://www.scons.org +# Basically, this script just /defines/ the components and how they +# fit together. SCons will derive the necessary build steps. + + +#-----------------------------------Configuration +TARGDIR = 'target' +VERSION = '0.pre.01' +TOOLDIR = './admin/scons' # SCons plugins +SCRIPTDIR = './admin' +OPTCACHE = 'optcache' +CUSTOPTFILE = 'custom-options' + +# these are accessible via env.path.xxxx +srcIcon = 'icons' +srcConf = 'data/config' +buildExe = '#$TARGDIR' +buildLib = '#$TARGDIR/modules' +buildPlug = '#$TARGDIR/modules' +buildIcon = '#$TARGDIR/gui/icons' +buildUIRes = '#$TARGDIR/' +buildConf = '#$TARGDIR/config' +installExe = '#$DESTDIR/lib/lumiera' +installLib = '#$DESTDIR/lib/lumiera/modules' +installPlug = '#$DESTDIR/lib/lumiera/modules' +installIcon = '#$DESTDIR/share/lumiera/icons' +installUIRes = '#$DESTDIR/share/lumiera/' +installConf = '#$DESTDIR/lib/lumiera/config' + +#-----------------------------------Configuration +localDefinitions = locals() + + + + +import os +import sys + +sys.path.append(TOOLDIR) +sys.path.append(SCRIPTDIR) + +from Buildhelper import * +from LumieraEnvironment import * + + +##################################################################### + +def setupBasicEnvironment(localDefinitions): + """ define cmdline options, build type decisions + """ + EnsurePythonVersion(2,4) + EnsureSConsVersion(1,0) + + Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 + + vars = defineCmdlineVariables() + env = LumieraEnvironment(variables=vars + ,toolpath = [TOOLDIR] + ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe + ,TARGDIR = TARGDIR + ,DESTDIR = '$INSTALLDIR/$PREFIX' + ,VERSION = VERSION + ) + handleVerboseMessages(env) + + env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root + , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines + , CCFLAGS='-Wall -Wextra ' + , CFLAGS='-std=gnu99' + ) + handleNoBugSwitches(env) + + env.Append(CPPDEFINES = '_GNU_SOURCE') + appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') +# appendCppDefine(env,'OPENGL','USE_OPENGL') + appendVal(env,'ARCHFLAGS','CCFLAGS') # for both C and C++ + appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') + appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') + + # setup search path for Lumiera plugins + appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:ORIGIN/modules\\"' + ,'LUMIERA_PLUGIN_PATH=\\"ORIGIN/modules\\"') + appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' + ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') + + prepareOptionsHelp(vars,env) + vars.Save(OPTCACHE, env) + return env + +def appendCppDefine(env,var,cppVar, elseVal=''): + if env[var]: + env.Append(CPPDEFINES = env.subst(cppVar) ) + elif elseVal: + env.Append(CPPDEFINES = env.subst(elseVal)) + +def appendVal(env,var,targetVar,val=None): + if env[var]: + env.Append( **{targetVar: env.subst(val) or env[var]}) + + +def handleNoBugSwitches(env): + """ set the build level for NoBug. + Release builds imply no DEBUG + whereas ALPHA and BETA require DEBUG + """ + level = env['BUILDLEVEL'] + if level in ['ALPHA', 'BETA']: + if not env['DEBUG']: + print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!' + env.Replace( DEBUG = 1 ) + env.Append(CPPDEFINES = 'EBUG_'+level) + elif level == 'RELEASE': + env.Replace( DEBUG = 0 ) + +def handleVerboseMessages(env): + """ toggle verbose build output """ + if not env['VERBOSE']: + # SetOption('silent', True) + env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" + env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" + env['LINKCOMSTR'] = " Linking --> $TARGET" + env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" + + + + +def defineCmdlineVariables(): + """ several toggles and configuration variables can be set on the commandline, + current settings will be persisted in a options cache file. + you may define custom variable settings in a separate file. + Commandline will override both. + """ + vars = Variables([OPTCACHE, CUSTOPTFILE]) + vars.AddVariables( + ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') + ,('CC', 'Set the C compiler to use.', 'gcc') + ,('CXX', 'Set the C++ compiler to use.', 'g++') + ,PathVariable('CCACHE', 'Integrate with CCache', '', PathVariable.PathAccept) + ,PathVariable('DISTCC', 'Invoke C/C++ compiler commands through DistCC', '', PathVariable.PathAccept) + ,EnumVariable('BUILDLEVEL', 'NoBug build level for debugging', 'ALPHA', allowed_values=('ALPHA', 'BETA', 'RELEASE')) + ,BoolVariable('DEBUG', 'Build with debugging information and no optimisations', False) + ,BoolVariable('OPTIMIZE', 'Build with strong optimisation (-O3)', False) + ,BoolVariable('VALGRIND', 'Run Testsuite under valgrind control', True) + ,BoolVariable('VERBOSE', 'Print full build commands', False) + ,('TESTSUITES', 'Run only Testsuites matching the given pattern', '') +# ,BoolVariable('OPENGL', 'Include support for OpenGL preview rendering', False) +# ,EnumVariable('DIST_TARGET', 'Build target architecture', 'auto', +# allowed_values=('auto', 'i386', 'i686', 'x86_64' ), ignorecase=2) + ,PathVariable('PREFIX', 'Installation dir prefix', 'usr/local', PathVariable.PathAccept) + ,PathVariable('INSTALLDIR', 'Root output directory for install. Final installation will happen in INSTALLDIR/PREFIX/... ', '/', PathVariable.PathIsDir) + ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) + ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) + ) + + return vars + + + +def prepareOptionsHelp(vars,env): + prelude = """ +USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] + Build and optionally install Lumiera. + Without specifying any target, just the (re)build target will run. + Add -c to the commandline to clean up anything a given target would produce + +Special Targets: + build : just compile and link + research: build experimental code (might fail) + testcode: additionally compile the Testsuite + check : build and run the Testsuite + doc : generate documentation (Doxygen) + all : build and testcode and doc + install : install created artifacts at PREFIX + +Configuration Options: +""" + Help(prelude + vars.GenerateHelpText(env)) + + + + +def configurePlatform(env): + """ locate required libs. + setup platform specific options. + Abort build in case of failure. + """ + conf = env.Configure() + # run all configuration checks in the given env + + # Perform checks for prerequisites -------------------------------------------- + problems = [] + if not conf.TryAction('pkg-config --version > $TARGET')[0]: + problems.append('We need pkg-config for including library configurations, exiting.') + + if not conf.CheckLibWithHeader('m', 'math.h','C'): + problems.append('Did not find math.h / libm.') + + if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): + problems.append('Functions for runtime dynamic loading not available.') + + if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): + problems.append('Did not find the pthread lib or pthread.h.') + else: + conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') + conf.env.Append(CCFLAGS = ' -pthread') + + if conf.CheckCHeader('execinfo.h'): + conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') + + if conf.CheckCHeader('valgrind/valgrind.h'): + conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') + else: + print 'Valgrind not found. The use of Valgrind is optional; building without.' + + if not conf.CheckPkgConfig('nobugmt', 201006.1): + problems.append('Did not find NoBug [http://www.lumiera.org/nobug_manual.html].') + else: + conf.env.mergeConf('nobugmt') + + if not conf.CheckCXXHeader('tr1/memory'): + problems.append('We rely on the std::tr1 standard C++ extension for shared_ptr.') + + if not conf.CheckCXXHeader('boost/config.hpp'): + problems.append('We need the C++ boost-libraries.') + else: + if not conf.CheckCXXHeader('boost/scoped_ptr.hpp'): + problems.append('We need boost::scoped_ptr (scoped_ptr.hpp).') + if not conf.CheckCXXHeader('boost/format.hpp'): + problems.append('We need boost::format (header).') + if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): + problems.append('We need boost::program_options (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'): + problems.append('We need the boost::system support library (including binary lib).') + if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): + problems.append('We need the boost::filesystem lib (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): + problems.append('We need the boost regular expression lib (incl. binary lib for linking).') + + + if conf.CheckLib(symbol='clock_gettime'): + print 'Using function clock_gettime() as defined in the C-lib...' + else: + if not conf.CheckLib(symbol='clock_gettime', library='rt'): + problems.append('No library known to provide the clock_gettime() function.') + + if not conf.CheckPkgConfig('gavl', 1.0): + problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') + else: + conf.env.mergeConf('gavl') + + if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): + problems.append('Unable to configure GTK--') + + if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): + problems.append('Unable to configure Lib glib--') + + if not conf.CheckPkgConfig('gthread-2.0', '2.12.4'): + problems.append('Need gthread support lib for glib-- based thread handling.') + + if not conf.CheckPkgConfig('cairomm-1.0', 0.6): + problems.append('Unable to configure Cairo--') + + verGDL = '2.27.1' + if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): + print 'No sufficiently recent (>=%s) version of GDL found. Maybe use custom package gdl-lum?' % verGDL + if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): + problems.append('GNOME Docking Library not found. We either need a sufficiently recent GDL ' + 'version (>=%s), or the custom package "gdl-lum" from Lumiera.org.' % verGDL) + + if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): + problems.append('Need rsvg Library for rendering icons.') + + if not conf.CheckCHeader(['X11/Xutil.h', 'X11/Xlib.h'],'<>'): + problems.append('Xlib.h and Xutil.h required. Please install libx11-dev.') + + if not conf.CheckPkgConfig('xv') : problems.append('Need libXv...') + if not conf.CheckPkgConfig('xext'): problems.append('Need libXext.') + + + # report missing dependencies + if problems: + print "*** unable to build due to the following problems:" + for isue in problems: + print " * %s" % isue + print + print "build aborted." + Exit(1) + + print "** Gathered Library Info: %s" % conf.env.libInfo.keys() + + + # create new env containing the finished configuration + return conf.Finish() + + + +def defineSetupTargets(env): + """ build operations and targets to be done /before/ compiling. + things like creating a source tarball or preparing a version header. + """ + pass ## currently none + + + +def defineBuildTargets(env): + """ define the source file/dirs comprising each artifact to be built. + setup sub-environments with special build options if necessary. + We use a custom function to declare a whole tree of srcfiles. + """ + + # define Icons to render and install + vector_icon_dir = env.subst(env.path.srcIcon+'svg') + prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') + icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + + #define Configuration files to install + config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + ) + + # call subdir SConscript(s) for independent components + SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') + + + +def definePostBuildTargets(env): + """ define further actions after the core build (e.g. Documentation). + define alias targets to trigger the installing. + """ + Import('lumiera plugins tools gui testsuite') + build = env.Alias('build', lumiera + plugins + tools +gui) + + # additional files to be cleaned when cleaning 'build' + env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) + env.Clean ('build', [ 'src/pre.gch' ]) + + doxydoc = env.Doxygen('doc/devel/Doxyfile') + env.Alias ('doc', doxydoc) + env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) + + env.Alias ('all', build + testsuite + doxydoc) + env.Default('build') + # SCons default target + + +def defineInstallTargets(env): + """ define additional artifacts to be installed into target locations. + @note: we use customised SCons builders defining install targets + for all executables automatically. see LumieraEnvironment.py + """ + Import('lumiera plugins tools gui testsuite') + + env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') +# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) + + env.Alias('install', gui) + env.Alias('install', '$DESTDIR') + +##################################################################### + + + + + +### === MAIN === #################################################### + +env = setupBasicEnvironment(localDefinitions) + +if not (isCleanupOperation(env) or isHelpRequest()): + env = configurePlatform(env) + +# the various things we build. +# Each entry actually is a SCons-Node list. +# Passing these entries to other builders defines dependencies. +# 'lumiera' : the App +# 'gui' : the GTK UI (plugin) +# 'plugins' : plugin shared lib +# 'tools' : small tool applications (e.g mpegtoc) + +defineSetupTargets(env) +defineBuildTargets(env) +definePostBuildTargets(env) +defineInstallTargets(env) + From 795217b542a764f92a170794707786f08f8ff5f8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 9 Jan 2012 04:58:21 +0100 Subject: [PATCH 283/296] actually switch to using those extracted modules from main build --- SConstruct | 284 +---------------------------------- admin/scons/Options.py | 323 +--------------------------------------- admin/scons/Platform.py | 257 +------------------------------- admin/scons/Setup.py | 282 ++--------------------------------- 4 files changed, 18 insertions(+), 1128 deletions(-) diff --git a/SConstruct b/SConstruct index cd8165eae..ccb0a73a7 100644 --- a/SConstruct +++ b/SConstruct @@ -29,31 +29,9 @@ #-----------------------------------Configuration -TARGDIR = 'target' -VERSION = '0.pre.01' TOOLDIR = './admin/scons' # SCons plugins SCRIPTDIR = './admin' -OPTCACHE = 'optcache' -CUSTOPTFILE = 'custom-options' - -# these are accessible via env.path.xxxx -srcIcon = 'icons' -srcConf = 'data/config' -buildExe = '#$TARGDIR' -buildLib = '#$TARGDIR/modules' -buildPlug = '#$TARGDIR/modules' -buildIcon = '#$TARGDIR/gui/icons' -buildUIRes = '#$TARGDIR/' -buildConf = '#$TARGDIR/config' -installExe = '#$DESTDIR/lib/lumiera' -installLib = '#$DESTDIR/lib/lumiera/modules' -installPlug = '#$DESTDIR/lib/lumiera/modules' -installIcon = '#$DESTDIR/share/lumiera/icons' -installUIRes = '#$DESTDIR/share/lumiera/' -installConf = '#$DESTDIR/lib/lumiera/config' - #-----------------------------------Configuration -localDefinitions = locals() @@ -72,264 +50,9 @@ import Setup import Options import Platform + ##################################################################### -def setupBasicEnvironment(localDefinitions): - """ define cmdline options, build type decisions - """ - EnsurePythonVersion(2,4) - EnsureSConsVersion(1,0) - - Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 - - vars = defineCmdlineVariables() - env = LumieraEnvironment(variables=vars - ,toolpath = [TOOLDIR] - ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe - ,TARGDIR = TARGDIR - ,DESTDIR = '$INSTALLDIR/$PREFIX' - ,VERSION = VERSION - ) - handleVerboseMessages(env) - - env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root - , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines - , CCFLAGS='-Wall -Wextra ' - , CFLAGS='-std=gnu99' - ) - handleNoBugSwitches(env) - - env.Append(CPPDEFINES = '_GNU_SOURCE') - appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') -# appendCppDefine(env,'OPENGL','USE_OPENGL') - appendVal(env,'ARCHFLAGS','CCFLAGS') # for both C and C++ - appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') - appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') - - # setup search path for Lumiera plugins - appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:ORIGIN/modules\\"' - ,'LUMIERA_PLUGIN_PATH=\\"ORIGIN/modules\\"') - appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' - ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') - - prepareOptionsHelp(vars,env) - vars.Save(OPTCACHE, env) - return env - -def appendCppDefine(env,var,cppVar, elseVal=''): - if env[var]: - env.Append(CPPDEFINES = env.subst(cppVar) ) - elif elseVal: - env.Append(CPPDEFINES = env.subst(elseVal)) - -def appendVal(env,var,targetVar,val=None): - if env[var]: - env.Append( **{targetVar: env.subst(val) or env[var]}) - - -def handleNoBugSwitches(env): - """ set the build level for NoBug. - Release builds imply no DEBUG - whereas ALPHA and BETA require DEBUG - """ - level = env['BUILDLEVEL'] - if level in ['ALPHA', 'BETA']: - if not env['DEBUG']: - print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!' - env.Replace( DEBUG = 1 ) - env.Append(CPPDEFINES = 'EBUG_'+level) - elif level == 'RELEASE': - env.Replace( DEBUG = 0 ) - -def handleVerboseMessages(env): - """ toggle verbose build output """ - if not env['VERBOSE']: - # SetOption('silent', True) - env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" - env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" - env['LINKCOMSTR'] = " Linking --> $TARGET" - env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" - - - - -def defineCmdlineVariables(): - """ several toggles and configuration variables can be set on the commandline, - current settings will be persisted in a options cache file. - you may define custom variable settings in a separate file. - Commandline will override both. - """ - vars = Variables([OPTCACHE, CUSTOPTFILE]) - vars.AddVariables( - ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') - ,('CC', 'Set the C compiler to use.', 'gcc') - ,('CXX', 'Set the C++ compiler to use.', 'g++') - ,PathVariable('CCACHE', 'Integrate with CCache', '', PathVariable.PathAccept) - ,PathVariable('DISTCC', 'Invoke C/C++ compiler commands through DistCC', '', PathVariable.PathAccept) - ,EnumVariable('BUILDLEVEL', 'NoBug build level for debugging', 'ALPHA', allowed_values=('ALPHA', 'BETA', 'RELEASE')) - ,BoolVariable('DEBUG', 'Build with debugging information and no optimisations', False) - ,BoolVariable('OPTIMIZE', 'Build with strong optimisation (-O3)', False) - ,BoolVariable('VALGRIND', 'Run Testsuite under valgrind control', True) - ,BoolVariable('VERBOSE', 'Print full build commands', False) - ,('TESTSUITES', 'Run only Testsuites matching the given pattern', '') -# ,BoolVariable('OPENGL', 'Include support for OpenGL preview rendering', False) -# ,EnumVariable('DIST_TARGET', 'Build target architecture', 'auto', -# allowed_values=('auto', 'i386', 'i686', 'x86_64' ), ignorecase=2) - ,PathVariable('PREFIX', 'Installation dir prefix', 'usr/local', PathVariable.PathAccept) - ,PathVariable('INSTALLDIR', 'Root output directory for install. Final installation will happen in INSTALLDIR/PREFIX/... ', '/', PathVariable.PathIsDir) - ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) - ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) - ) - - return vars - - - -def prepareOptionsHelp(vars,env): - prelude = """ -USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] - Build and optionally install Lumiera. - Without specifying any target, just the (re)build target will run. - Add -c to the commandline to clean up anything a given target would produce - -Special Targets: - build : just compile and link - research: build experimental code (might fail) - testcode: additionally compile the Testsuite - check : build and run the Testsuite - doc : generate documentation (Doxygen) - all : build and testcode and doc - install : install created artifacts at PREFIX - -Configuration Options: -""" - Help(prelude + vars.GenerateHelpText(env)) - - - - -def configurePlatform(env): - """ locate required libs. - setup platform specific options. - Abort build in case of failure. - """ - conf = env.Configure() - # run all configuration checks in the given env - - # Perform checks for prerequisites -------------------------------------------- - problems = [] - if not conf.TryAction('pkg-config --version > $TARGET')[0]: - problems.append('We need pkg-config for including library configurations, exiting.') - - if not conf.CheckLibWithHeader('m', 'math.h','C'): - problems.append('Did not find math.h / libm.') - - if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): - problems.append('Functions for runtime dynamic loading not available.') - - if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): - problems.append('Did not find the pthread lib or pthread.h.') - else: - conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') - conf.env.Append(CCFLAGS = ' -pthread') - - if conf.CheckCHeader('execinfo.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') - - if conf.CheckCHeader('valgrind/valgrind.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') - else: - print 'Valgrind not found. The use of Valgrind is optional; building without.' - - if not conf.CheckPkgConfig('nobugmt', 201006.1): - problems.append('Did not find NoBug [http://www.lumiera.org/nobug_manual.html].') - else: - conf.env.mergeConf('nobugmt') - - if not conf.CheckCXXHeader('tr1/memory'): - problems.append('We rely on the std::tr1 standard C++ extension for shared_ptr.') - - if not conf.CheckCXXHeader('boost/config.hpp'): - problems.append('We need the C++ boost-libraries.') - else: - if not conf.CheckCXXHeader('boost/scoped_ptr.hpp'): - problems.append('We need boost::scoped_ptr (scoped_ptr.hpp).') - if not conf.CheckCXXHeader('boost/format.hpp'): - problems.append('We need boost::format (header).') - if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): - problems.append('We need boost::program_options (including binary lib for linking).') - if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'): - problems.append('We need the boost::system support library (including binary lib).') - if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): - problems.append('We need the boost::filesystem lib (including binary lib for linking).') - if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): - problems.append('We need the boost regular expression lib (incl. binary lib for linking).') - - - if conf.CheckLib(symbol='clock_gettime'): - print 'Using function clock_gettime() as defined in the C-lib...' - else: - if not conf.CheckLib(symbol='clock_gettime', library='rt'): - problems.append('No library known to provide the clock_gettime() function.') - - if not conf.CheckPkgConfig('gavl', 1.0): - problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') - else: - conf.env.mergeConf('gavl') - - if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): - problems.append('Unable to configure GTK--') - - if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): - problems.append('Unable to configure Lib glib--') - - if not conf.CheckPkgConfig('gthread-2.0', '2.12.4'): - problems.append('Need gthread support lib for glib-- based thread handling.') - - if not conf.CheckPkgConfig('cairomm-1.0', 0.6): - problems.append('Unable to configure Cairo--') - - verGDL = '2.27.1' - if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): - print 'No sufficiently recent (>=%s) version of GDL found. Maybe use custom package gdl-lum?' % verGDL - if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): - problems.append('GNOME Docking Library not found. We either need a sufficiently recent GDL ' - 'version (>=%s), or the custom package "gdl-lum" from Lumiera.org.' % verGDL) - - if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): - problems.append('Need rsvg Library for rendering icons.') - - if not conf.CheckCHeader(['X11/Xutil.h', 'X11/Xlib.h'],'<>'): - problems.append('Xlib.h and Xutil.h required. Please install libx11-dev.') - - if not conf.CheckPkgConfig('xv') : problems.append('Need libXv...') - if not conf.CheckPkgConfig('xext'): problems.append('Need libXext.') - - - # report missing dependencies - if problems: - print "*** unable to build due to the following problems:" - for isue in problems: - print " * %s" % isue - print - print "build aborted." - Exit(1) - - print "** Gathered Library Info: %s" % conf.env.libInfo.keys() - - - # create new env containing the finished configuration - return conf.Finish() - - - -def defineSetupTargets(env): - """ build operations and targets to be done /before/ compiling. - things like creating a source tarball or preparing a version header. - """ - pass ## currently none - - def defineBuildTargets(env): """ define the source file/dirs comprising each artifact to be built. @@ -395,10 +118,10 @@ def defineInstallTargets(env): ### === MAIN === #################################################### -env = setupBasicEnvironment(localDefinitions) +env = Setup.setupBasicEnvironment() if not (isCleanupOperation(env) or isHelpRequest()): - env = configurePlatform(env) + env = Platform.configurePlatform(env) # the various things we build. # Each entry actually is a SCons-Node list. @@ -408,7 +131,6 @@ if not (isCleanupOperation(env) or isHelpRequest()): # 'plugins' : plugin shared lib # 'tools' : small tool applications (e.g mpegtoc) -defineSetupTargets(env) defineBuildTargets(env) definePostBuildTargets(env) defineInstallTargets(env) diff --git a/admin/scons/Options.py b/admin/scons/Options.py index f919995de..ed1c8bb93 100644 --- a/admin/scons/Options.py +++ b/admin/scons/Options.py @@ -22,47 +22,11 @@ ##################################################################### -# NOTE: scons -h for help. -# Read more about the SCons build system at: http://www.scons.org -# Basically, this script just /defines/ the components and how they -# fit together. SCons will derive the necessary build steps. - - -#-----------------------------------Configuration -TARGDIR = 'target' -VERSION = '0.pre.01' -TOOLDIR = './admin/scons' # SCons plugins -SCRIPTDIR = './admin' -OPTCACHE = 'optcache' -CUSTOPTFILE = 'custom-options' - -# these are accessible via env.path.xxxx -srcIcon = 'icons' -srcConf = 'data/config' -buildExe = '#$TARGDIR' -buildLib = '#$TARGDIR/modules' -buildPlug = '#$TARGDIR/modules' -buildIcon = '#$TARGDIR/gui/icons' -buildUIRes = '#$TARGDIR/' -buildConf = '#$TARGDIR/config' -installExe = '#$DESTDIR/lib/lumiera' -installLib = '#$DESTDIR/lib/lumiera/modules' -installPlug = '#$DESTDIR/lib/lumiera/modules' -installIcon = '#$DESTDIR/share/lumiera/icons' -installUIRes = '#$DESTDIR/share/lumiera/' -installConf = '#$DESTDIR/lib/lumiera/config' - -#-----------------------------------Configuration -localDefinitions = locals() - - - import os import sys -sys.path.append(TOOLDIR) -sys.path.append(SCRIPTDIR) +from SCons.Script import * from Buildhelper import * from LumieraEnvironment import * @@ -70,92 +34,15 @@ from LumieraEnvironment import * ##################################################################### -def setupBasicEnvironment(localDefinitions): - """ define cmdline options, build type decisions - """ - EnsurePythonVersion(2,4) - EnsureSConsVersion(1,0) - - Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 - - vars = defineCmdlineVariables() - env = LumieraEnvironment(variables=vars - ,toolpath = [TOOLDIR] - ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe - ,TARGDIR = TARGDIR - ,DESTDIR = '$INSTALLDIR/$PREFIX' - ,VERSION = VERSION - ) - handleVerboseMessages(env) - - env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root - , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines - , CCFLAGS='-Wall -Wextra ' - , CFLAGS='-std=gnu99' - ) - handleNoBugSwitches(env) - - env.Append(CPPDEFINES = '_GNU_SOURCE') - appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') -# appendCppDefine(env,'OPENGL','USE_OPENGL') - appendVal(env,'ARCHFLAGS','CCFLAGS') # for both C and C++ - appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') - appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') - - # setup search path for Lumiera plugins - appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:ORIGIN/modules\\"' - ,'LUMIERA_PLUGIN_PATH=\\"ORIGIN/modules\\"') - appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' - ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') - - prepareOptionsHelp(vars,env) - vars.Save(OPTCACHE, env) - return env - -def appendCppDefine(env,var,cppVar, elseVal=''): - if env[var]: - env.Append(CPPDEFINES = env.subst(cppVar) ) - elif elseVal: - env.Append(CPPDEFINES = env.subst(elseVal)) - -def appendVal(env,var,targetVar,val=None): - if env[var]: - env.Append( **{targetVar: env.subst(val) or env[var]}) - - -def handleNoBugSwitches(env): - """ set the build level for NoBug. - Release builds imply no DEBUG - whereas ALPHA and BETA require DEBUG - """ - level = env['BUILDLEVEL'] - if level in ['ALPHA', 'BETA']: - if not env['DEBUG']: - print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!' - env.Replace( DEBUG = 1 ) - env.Append(CPPDEFINES = 'EBUG_'+level) - elif level == 'RELEASE': - env.Replace( DEBUG = 0 ) - -def handleVerboseMessages(env): - """ toggle verbose build output """ - if not env['VERBOSE']: - # SetOption('silent', True) - env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" - env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" - env['LINKCOMSTR'] = " Linking --> $TARGET" - env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" - -def defineCmdlineVariables(): +def defineCmdlineVariables(vars): """ several toggles and configuration variables can be set on the commandline, current settings will be persisted in a options cache file. you may define custom variable settings in a separate file. Commandline will override both. """ - vars = Variables([OPTCACHE, CUSTOPTFILE]) vars.AddVariables( ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') ,('CC', 'Set the C compiler to use.', 'gcc') @@ -203,209 +90,3 @@ Configuration Options: - -def configurePlatform(env): - """ locate required libs. - setup platform specific options. - Abort build in case of failure. - """ - conf = env.Configure() - # run all configuration checks in the given env - - # Perform checks for prerequisites -------------------------------------------- - problems = [] - if not conf.TryAction('pkg-config --version > $TARGET')[0]: - problems.append('We need pkg-config for including library configurations, exiting.') - - if not conf.CheckLibWithHeader('m', 'math.h','C'): - problems.append('Did not find math.h / libm.') - - if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): - problems.append('Functions for runtime dynamic loading not available.') - - if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): - problems.append('Did not find the pthread lib or pthread.h.') - else: - conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') - conf.env.Append(CCFLAGS = ' -pthread') - - if conf.CheckCHeader('execinfo.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') - - if conf.CheckCHeader('valgrind/valgrind.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') - else: - print 'Valgrind not found. The use of Valgrind is optional; building without.' - - if not conf.CheckPkgConfig('nobugmt', 201006.1): - problems.append('Did not find NoBug [http://www.lumiera.org/nobug_manual.html].') - else: - conf.env.mergeConf('nobugmt') - - if not conf.CheckCXXHeader('tr1/memory'): - problems.append('We rely on the std::tr1 standard C++ extension for shared_ptr.') - - if not conf.CheckCXXHeader('boost/config.hpp'): - problems.append('We need the C++ boost-libraries.') - else: - if not conf.CheckCXXHeader('boost/scoped_ptr.hpp'): - problems.append('We need boost::scoped_ptr (scoped_ptr.hpp).') - if not conf.CheckCXXHeader('boost/format.hpp'): - problems.append('We need boost::format (header).') - if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): - problems.append('We need boost::program_options (including binary lib for linking).') - if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'): - problems.append('We need the boost::system support library (including binary lib).') - if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): - problems.append('We need the boost::filesystem lib (including binary lib for linking).') - if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): - problems.append('We need the boost regular expression lib (incl. binary lib for linking).') - - - if conf.CheckLib(symbol='clock_gettime'): - print 'Using function clock_gettime() as defined in the C-lib...' - else: - if not conf.CheckLib(symbol='clock_gettime', library='rt'): - problems.append('No library known to provide the clock_gettime() function.') - - if not conf.CheckPkgConfig('gavl', 1.0): - problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') - else: - conf.env.mergeConf('gavl') - - if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): - problems.append('Unable to configure GTK--') - - if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): - problems.append('Unable to configure Lib glib--') - - if not conf.CheckPkgConfig('gthread-2.0', '2.12.4'): - problems.append('Need gthread support lib for glib-- based thread handling.') - - if not conf.CheckPkgConfig('cairomm-1.0', 0.6): - problems.append('Unable to configure Cairo--') - - verGDL = '2.27.1' - if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): - print 'No sufficiently recent (>=%s) version of GDL found. Maybe use custom package gdl-lum?' % verGDL - if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): - problems.append('GNOME Docking Library not found. We either need a sufficiently recent GDL ' - 'version (>=%s), or the custom package "gdl-lum" from Lumiera.org.' % verGDL) - - if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): - problems.append('Need rsvg Library for rendering icons.') - - if not conf.CheckCHeader(['X11/Xutil.h', 'X11/Xlib.h'],'<>'): - problems.append('Xlib.h and Xutil.h required. Please install libx11-dev.') - - if not conf.CheckPkgConfig('xv') : problems.append('Need libXv...') - if not conf.CheckPkgConfig('xext'): problems.append('Need libXext.') - - - # report missing dependencies - if problems: - print "*** unable to build due to the following problems:" - for isue in problems: - print " * %s" % isue - print - print "build aborted." - Exit(1) - - print "** Gathered Library Info: %s" % conf.env.libInfo.keys() - - - # create new env containing the finished configuration - return conf.Finish() - - - -def defineSetupTargets(env): - """ build operations and targets to be done /before/ compiling. - things like creating a source tarball or preparing a version header. - """ - pass ## currently none - - - -def defineBuildTargets(env): - """ define the source file/dirs comprising each artifact to be built. - setup sub-environments with special build options if necessary. - We use a custom function to declare a whole tree of srcfiles. - """ - - # define Icons to render and install - vector_icon_dir = env.subst(env.path.srcIcon+'svg') - prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') - icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] - + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) - - #define Configuration files to install - config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) - - # call subdir SConscript(s) for independent components - SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') - - - -def definePostBuildTargets(env): - """ define further actions after the core build (e.g. Documentation). - define alias targets to trigger the installing. - """ - Import('lumiera plugins tools gui testsuite') - build = env.Alias('build', lumiera + plugins + tools +gui) - - # additional files to be cleaned when cleaning 'build' - env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) - env.Clean ('build', [ 'src/pre.gch' ]) - - doxydoc = env.Doxygen('doc/devel/Doxyfile') - env.Alias ('doc', doxydoc) - env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - - env.Alias ('all', build + testsuite + doxydoc) - env.Default('build') - # SCons default target - - -def defineInstallTargets(env): - """ define additional artifacts to be installed into target locations. - @note: we use customised SCons builders defining install targets - for all executables automatically. see LumieraEnvironment.py - """ - Import('lumiera plugins tools gui testsuite') - - env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') -# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) - - env.Alias('install', gui) - env.Alias('install', '$DESTDIR') - -##################################################################### - - - - - -### === MAIN === #################################################### - -env = setupBasicEnvironment(localDefinitions) - -if not (isCleanupOperation(env) or isHelpRequest()): - env = configurePlatform(env) - -# the various things we build. -# Each entry actually is a SCons-Node list. -# Passing these entries to other builders defines dependencies. -# 'lumiera' : the App -# 'gui' : the GTK UI (plugin) -# 'plugins' : plugin shared lib -# 'tools' : small tool applications (e.g mpegtoc) - -defineSetupTargets(env) -defineBuildTargets(env) -definePostBuildTargets(env) -defineInstallTargets(env) - diff --git a/admin/scons/Platform.py b/admin/scons/Platform.py index e8f9d050b..4d5d12e7b 100644 --- a/admin/scons/Platform.py +++ b/admin/scons/Platform.py @@ -28,179 +28,16 @@ # fit together. SCons will derive the necessary build steps. -#-----------------------------------Configuration -TARGDIR = 'target' -VERSION = '0.pre.01' -TOOLDIR = './admin/scons' # SCons plugins -SCRIPTDIR = './admin' -OPTCACHE = 'optcache' -CUSTOPTFILE = 'custom-options' - -# these are accessible via env.path.xxxx -srcIcon = 'icons' -srcConf = 'data/config' -buildExe = '#$TARGDIR' -buildLib = '#$TARGDIR/modules' -buildPlug = '#$TARGDIR/modules' -buildIcon = '#$TARGDIR/gui/icons' -buildUIRes = '#$TARGDIR/' -buildConf = '#$TARGDIR/config' -installExe = '#$DESTDIR/lib/lumiera' -installLib = '#$DESTDIR/lib/lumiera/modules' -installPlug = '#$DESTDIR/lib/lumiera/modules' -installIcon = '#$DESTDIR/share/lumiera/icons' -installUIRes = '#$DESTDIR/share/lumiera/' -installConf = '#$DESTDIR/lib/lumiera/config' - -#-----------------------------------Configuration -localDefinitions = locals() - - - import os import sys -sys.path.append(TOOLDIR) -sys.path.append(SCRIPTDIR) +from SCons.Script import Exit from Buildhelper import * from LumieraEnvironment import * -##################################################################### - -def setupBasicEnvironment(localDefinitions): - """ define cmdline options, build type decisions - """ - EnsurePythonVersion(2,4) - EnsureSConsVersion(1,0) - - Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 - - vars = defineCmdlineVariables() - env = LumieraEnvironment(variables=vars - ,toolpath = [TOOLDIR] - ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe - ,TARGDIR = TARGDIR - ,DESTDIR = '$INSTALLDIR/$PREFIX' - ,VERSION = VERSION - ) - handleVerboseMessages(env) - - env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root - , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines - , CCFLAGS='-Wall -Wextra ' - , CFLAGS='-std=gnu99' - ) - handleNoBugSwitches(env) - - env.Append(CPPDEFINES = '_GNU_SOURCE') - appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') -# appendCppDefine(env,'OPENGL','USE_OPENGL') - appendVal(env,'ARCHFLAGS','CCFLAGS') # for both C and C++ - appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') - appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') - - # setup search path for Lumiera plugins - appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:ORIGIN/modules\\"' - ,'LUMIERA_PLUGIN_PATH=\\"ORIGIN/modules\\"') - appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' - ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') - - prepareOptionsHelp(vars,env) - vars.Save(OPTCACHE, env) - return env - -def appendCppDefine(env,var,cppVar, elseVal=''): - if env[var]: - env.Append(CPPDEFINES = env.subst(cppVar) ) - elif elseVal: - env.Append(CPPDEFINES = env.subst(elseVal)) - -def appendVal(env,var,targetVar,val=None): - if env[var]: - env.Append( **{targetVar: env.subst(val) or env[var]}) - - -def handleNoBugSwitches(env): - """ set the build level for NoBug. - Release builds imply no DEBUG - whereas ALPHA and BETA require DEBUG - """ - level = env['BUILDLEVEL'] - if level in ['ALPHA', 'BETA']: - if not env['DEBUG']: - print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!' - env.Replace( DEBUG = 1 ) - env.Append(CPPDEFINES = 'EBUG_'+level) - elif level == 'RELEASE': - env.Replace( DEBUG = 0 ) - -def handleVerboseMessages(env): - """ toggle verbose build output """ - if not env['VERBOSE']: - # SetOption('silent', True) - env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" - env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" - env['LINKCOMSTR'] = " Linking --> $TARGET" - env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" - - - - -def defineCmdlineVariables(): - """ several toggles and configuration variables can be set on the commandline, - current settings will be persisted in a options cache file. - you may define custom variable settings in a separate file. - Commandline will override both. - """ - vars = Variables([OPTCACHE, CUSTOPTFILE]) - vars.AddVariables( - ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') - ,('CC', 'Set the C compiler to use.', 'gcc') - ,('CXX', 'Set the C++ compiler to use.', 'g++') - ,PathVariable('CCACHE', 'Integrate with CCache', '', PathVariable.PathAccept) - ,PathVariable('DISTCC', 'Invoke C/C++ compiler commands through DistCC', '', PathVariable.PathAccept) - ,EnumVariable('BUILDLEVEL', 'NoBug build level for debugging', 'ALPHA', allowed_values=('ALPHA', 'BETA', 'RELEASE')) - ,BoolVariable('DEBUG', 'Build with debugging information and no optimisations', False) - ,BoolVariable('OPTIMIZE', 'Build with strong optimisation (-O3)', False) - ,BoolVariable('VALGRIND', 'Run Testsuite under valgrind control', True) - ,BoolVariable('VERBOSE', 'Print full build commands', False) - ,('TESTSUITES', 'Run only Testsuites matching the given pattern', '') -# ,BoolVariable('OPENGL', 'Include support for OpenGL preview rendering', False) -# ,EnumVariable('DIST_TARGET', 'Build target architecture', 'auto', -# allowed_values=('auto', 'i386', 'i686', 'x86_64' ), ignorecase=2) - ,PathVariable('PREFIX', 'Installation dir prefix', 'usr/local', PathVariable.PathAccept) - ,PathVariable('INSTALLDIR', 'Root output directory for install. Final installation will happen in INSTALLDIR/PREFIX/... ', '/', PathVariable.PathIsDir) - ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) - ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) - ) - - return vars - - - -def prepareOptionsHelp(vars,env): - prelude = """ -USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] - Build and optionally install Lumiera. - Without specifying any target, just the (re)build target will run. - Add -c to the commandline to clean up anything a given target would produce - -Special Targets: - build : just compile and link - research: build experimental code (might fail) - testcode: additionally compile the Testsuite - check : build and run the Testsuite - doc : generate documentation (Doxygen) - all : build and testcode and doc - install : install created artifacts at PREFIX - -Configuration Options: -""" - Help(prelude + vars.GenerateHelpText(env)) - @@ -317,95 +154,3 @@ def configurePlatform(env): # create new env containing the finished configuration return conf.Finish() - - -def defineSetupTargets(env): - """ build operations and targets to be done /before/ compiling. - things like creating a source tarball or preparing a version header. - """ - pass ## currently none - - - -def defineBuildTargets(env): - """ define the source file/dirs comprising each artifact to be built. - setup sub-environments with special build options if necessary. - We use a custom function to declare a whole tree of srcfiles. - """ - - # define Icons to render and install - vector_icon_dir = env.subst(env.path.srcIcon+'svg') - prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') - icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] - + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) - - #define Configuration files to install - config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) - - # call subdir SConscript(s) for independent components - SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') - - - -def definePostBuildTargets(env): - """ define further actions after the core build (e.g. Documentation). - define alias targets to trigger the installing. - """ - Import('lumiera plugins tools gui testsuite') - build = env.Alias('build', lumiera + plugins + tools +gui) - - # additional files to be cleaned when cleaning 'build' - env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) - env.Clean ('build', [ 'src/pre.gch' ]) - - doxydoc = env.Doxygen('doc/devel/Doxyfile') - env.Alias ('doc', doxydoc) - env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - - env.Alias ('all', build + testsuite + doxydoc) - env.Default('build') - # SCons default target - - -def defineInstallTargets(env): - """ define additional artifacts to be installed into target locations. - @note: we use customised SCons builders defining install targets - for all executables automatically. see LumieraEnvironment.py - """ - Import('lumiera plugins tools gui testsuite') - - env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') -# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) - - env.Alias('install', gui) - env.Alias('install', '$DESTDIR') - -##################################################################### - - - - - -### === MAIN === #################################################### - -env = setupBasicEnvironment(localDefinitions) - -if not (isCleanupOperation(env) or isHelpRequest()): - env = configurePlatform(env) - -# the various things we build. -# Each entry actually is a SCons-Node list. -# Passing these entries to other builders defines dependencies. -# 'lumiera' : the App -# 'gui' : the GTK UI (plugin) -# 'plugins' : plugin shared lib -# 'tools' : small tool applications (e.g mpegtoc) - -defineSetupTargets(env) -defineBuildTargets(env) -definePostBuildTargets(env) -defineInstallTargets(env) - diff --git a/admin/scons/Setup.py b/admin/scons/Setup.py index d910b04de..23112a09b 100644 --- a/admin/scons/Setup.py +++ b/admin/scons/Setup.py @@ -61,8 +61,9 @@ localDefinitions = locals() import os import sys -sys.path.append(TOOLDIR) -sys.path.append(SCRIPTDIR) +from SCons.Script import * + +from Options import * from Buildhelper import * from LumieraEnvironment import * @@ -70,7 +71,7 @@ from LumieraEnvironment import * ##################################################################### -def setupBasicEnvironment(localDefinitions): +def setupBasicEnvironment(): """ define cmdline options, build type decisions """ EnsurePythonVersion(2,4) @@ -78,7 +79,9 @@ def setupBasicEnvironment(localDefinitions): Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 - vars = defineCmdlineVariables() + vars = Variables([OPTCACHE, CUSTOPTFILE]) + vars = defineCmdlineVariables(vars) + env = LumieraEnvironment(variables=vars ,toolpath = [TOOLDIR] ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe @@ -140,272 +143,11 @@ def handleNoBugSwitches(env): def handleVerboseMessages(env): """ toggle verbose build output """ if not env['VERBOSE']: - # SetOption('silent', True) - env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" - env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" - env['LINKCOMSTR'] = " Linking --> $TARGET" - env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" + # SetOption('silent', True) + env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" + env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" + env['LINKCOMSTR'] = " Linking --> $TARGET" + env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" - -def defineCmdlineVariables(): - """ several toggles and configuration variables can be set on the commandline, - current settings will be persisted in a options cache file. - you may define custom variable settings in a separate file. - Commandline will override both. - """ - vars = Variables([OPTCACHE, CUSTOPTFILE]) - vars.AddVariables( - ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') - ,('CC', 'Set the C compiler to use.', 'gcc') - ,('CXX', 'Set the C++ compiler to use.', 'g++') - ,PathVariable('CCACHE', 'Integrate with CCache', '', PathVariable.PathAccept) - ,PathVariable('DISTCC', 'Invoke C/C++ compiler commands through DistCC', '', PathVariable.PathAccept) - ,EnumVariable('BUILDLEVEL', 'NoBug build level for debugging', 'ALPHA', allowed_values=('ALPHA', 'BETA', 'RELEASE')) - ,BoolVariable('DEBUG', 'Build with debugging information and no optimisations', False) - ,BoolVariable('OPTIMIZE', 'Build with strong optimisation (-O3)', False) - ,BoolVariable('VALGRIND', 'Run Testsuite under valgrind control', True) - ,BoolVariable('VERBOSE', 'Print full build commands', False) - ,('TESTSUITES', 'Run only Testsuites matching the given pattern', '') -# ,BoolVariable('OPENGL', 'Include support for OpenGL preview rendering', False) -# ,EnumVariable('DIST_TARGET', 'Build target architecture', 'auto', -# allowed_values=('auto', 'i386', 'i686', 'x86_64' ), ignorecase=2) - ,PathVariable('PREFIX', 'Installation dir prefix', 'usr/local', PathVariable.PathAccept) - ,PathVariable('INSTALLDIR', 'Root output directory for install. Final installation will happen in INSTALLDIR/PREFIX/... ', '/', PathVariable.PathIsDir) - ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) - ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) - ) - - return vars - - - -def prepareOptionsHelp(vars,env): - prelude = """ -USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] - Build and optionally install Lumiera. - Without specifying any target, just the (re)build target will run. - Add -c to the commandline to clean up anything a given target would produce - -Special Targets: - build : just compile and link - research: build experimental code (might fail) - testcode: additionally compile the Testsuite - check : build and run the Testsuite - doc : generate documentation (Doxygen) - all : build and testcode and doc - install : install created artifacts at PREFIX - -Configuration Options: -""" - Help(prelude + vars.GenerateHelpText(env)) - - - - -def configurePlatform(env): - """ locate required libs. - setup platform specific options. - Abort build in case of failure. - """ - conf = env.Configure() - # run all configuration checks in the given env - - # Perform checks for prerequisites -------------------------------------------- - problems = [] - if not conf.TryAction('pkg-config --version > $TARGET')[0]: - problems.append('We need pkg-config for including library configurations, exiting.') - - if not conf.CheckLibWithHeader('m', 'math.h','C'): - problems.append('Did not find math.h / libm.') - - if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): - problems.append('Functions for runtime dynamic loading not available.') - - if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): - problems.append('Did not find the pthread lib or pthread.h.') - else: - conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') - conf.env.Append(CCFLAGS = ' -pthread') - - if conf.CheckCHeader('execinfo.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') - - if conf.CheckCHeader('valgrind/valgrind.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') - else: - print 'Valgrind not found. The use of Valgrind is optional; building without.' - - if not conf.CheckPkgConfig('nobugmt', 201006.1): - problems.append('Did not find NoBug [http://www.lumiera.org/nobug_manual.html].') - else: - conf.env.mergeConf('nobugmt') - - if not conf.CheckCXXHeader('tr1/memory'): - problems.append('We rely on the std::tr1 standard C++ extension for shared_ptr.') - - if not conf.CheckCXXHeader('boost/config.hpp'): - problems.append('We need the C++ boost-libraries.') - else: - if not conf.CheckCXXHeader('boost/scoped_ptr.hpp'): - problems.append('We need boost::scoped_ptr (scoped_ptr.hpp).') - if not conf.CheckCXXHeader('boost/format.hpp'): - problems.append('We need boost::format (header).') - if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): - problems.append('We need boost::program_options (including binary lib for linking).') - if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'): - problems.append('We need the boost::system support library (including binary lib).') - if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): - problems.append('We need the boost::filesystem lib (including binary lib for linking).') - if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): - problems.append('We need the boost regular expression lib (incl. binary lib for linking).') - - - if conf.CheckLib(symbol='clock_gettime'): - print 'Using function clock_gettime() as defined in the C-lib...' - else: - if not conf.CheckLib(symbol='clock_gettime', library='rt'): - problems.append('No library known to provide the clock_gettime() function.') - - if not conf.CheckPkgConfig('gavl', 1.0): - problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') - else: - conf.env.mergeConf('gavl') - - if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): - problems.append('Unable to configure GTK--') - - if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): - problems.append('Unable to configure Lib glib--') - - if not conf.CheckPkgConfig('gthread-2.0', '2.12.4'): - problems.append('Need gthread support lib for glib-- based thread handling.') - - if not conf.CheckPkgConfig('cairomm-1.0', 0.6): - problems.append('Unable to configure Cairo--') - - verGDL = '2.27.1' - if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): - print 'No sufficiently recent (>=%s) version of GDL found. Maybe use custom package gdl-lum?' % verGDL - if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): - problems.append('GNOME Docking Library not found. We either need a sufficiently recent GDL ' - 'version (>=%s), or the custom package "gdl-lum" from Lumiera.org.' % verGDL) - - if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): - problems.append('Need rsvg Library for rendering icons.') - - if not conf.CheckCHeader(['X11/Xutil.h', 'X11/Xlib.h'],'<>'): - problems.append('Xlib.h and Xutil.h required. Please install libx11-dev.') - - if not conf.CheckPkgConfig('xv') : problems.append('Need libXv...') - if not conf.CheckPkgConfig('xext'): problems.append('Need libXext.') - - - # report missing dependencies - if problems: - print "*** unable to build due to the following problems:" - for isue in problems: - print " * %s" % isue - print - print "build aborted." - Exit(1) - - print "** Gathered Library Info: %s" % conf.env.libInfo.keys() - - - # create new env containing the finished configuration - return conf.Finish() - - - -def defineSetupTargets(env): - """ build operations and targets to be done /before/ compiling. - things like creating a source tarball or preparing a version header. - """ - pass ## currently none - - - -def defineBuildTargets(env): - """ define the source file/dirs comprising each artifact to be built. - setup sub-environments with special build options if necessary. - We use a custom function to declare a whole tree of srcfiles. - """ - - # define Icons to render and install - vector_icon_dir = env.subst(env.path.srcIcon+'svg') - prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') - icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] - + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) - - #define Configuration files to install - config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) - - # call subdir SConscript(s) for independent components - SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') - - - -def definePostBuildTargets(env): - """ define further actions after the core build (e.g. Documentation). - define alias targets to trigger the installing. - """ - Import('lumiera plugins tools gui testsuite') - build = env.Alias('build', lumiera + plugins + tools +gui) - - # additional files to be cleaned when cleaning 'build' - env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) - env.Clean ('build', [ 'src/pre.gch' ]) - - doxydoc = env.Doxygen('doc/devel/Doxyfile') - env.Alias ('doc', doxydoc) - env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - - env.Alias ('all', build + testsuite + doxydoc) - env.Default('build') - # SCons default target - - -def defineInstallTargets(env): - """ define additional artifacts to be installed into target locations. - @note: we use customised SCons builders defining install targets - for all executables automatically. see LumieraEnvironment.py - """ - Import('lumiera plugins tools gui testsuite') - - env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') -# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) - - env.Alias('install', gui) - env.Alias('install', '$DESTDIR') - -##################################################################### - - - - - -### === MAIN === #################################################### - -env = setupBasicEnvironment(localDefinitions) - -if not (isCleanupOperation(env) or isHelpRequest()): - env = configurePlatform(env) - -# the various things we build. -# Each entry actually is a SCons-Node list. -# Passing these entries to other builders defines dependencies. -# 'lumiera' : the App -# 'gui' : the GTK UI (plugin) -# 'plugins' : plugin shared lib -# 'tools' : small tool applications (e.g mpegtoc) - -defineSetupTargets(env) -defineBuildTargets(env) -definePostBuildTargets(env) -defineInstallTargets(env) - From e64a17d1ba394d80c060261c1d65dea4bd74b81b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 9 Jan 2012 05:13:15 +0100 Subject: [PATCH 284/296] SConstruct: reduce the remaining defs no need to use functions anymore, since only the clean and phony targets are left --- SConstruct | 125 +++++++++++++++++++------------------------------ src/SConscript | 6 +++ 2 files changed, 55 insertions(+), 76 deletions(-) diff --git a/SConstruct b/SConstruct index ccb0a73a7..e081d9144 100644 --- a/SConstruct +++ b/SConstruct @@ -53,85 +53,58 @@ import Platform ##################################################################### - -def defineBuildTargets(env): - """ define the source file/dirs comprising each artifact to be built. - setup sub-environments with special build options if necessary. - We use a custom function to declare a whole tree of srcfiles. - """ - - # define Icons to render and install - vector_icon_dir = env.subst(env.path.srcIcon+'svg') - prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') - icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] - + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) - - #define Configuration files to install - config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) - - # call subdir SConscript(s) for independent components - SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') - - - -def definePostBuildTargets(env): - """ define further actions after the core build (e.g. Documentation). - define alias targets to trigger the installing. - """ - Import('lumiera plugins tools gui testsuite') - build = env.Alias('build', lumiera + plugins + tools +gui) - - # additional files to be cleaned when cleaning 'build' - env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) - env.Clean ('build', [ 'src/pre.gch' ]) - - doxydoc = env.Doxygen('doc/devel/Doxyfile') - env.Alias ('doc', doxydoc) - env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - - env.Alias ('all', build + testsuite + doxydoc) - env.Default('build') - # SCons default target - - -def defineInstallTargets(env): - """ define additional artifacts to be installed into target locations. - @note: we use customised SCons builders defining install targets - for all executables automatically. see LumieraEnvironment.py - """ - Import('lumiera plugins tools gui testsuite') - - env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') -# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) - - env.Alias('install', gui) - env.Alias('install', '$DESTDIR') - -##################################################################### - - - - - -### === MAIN === #################################################### - env = Setup.setupBasicEnvironment() if not (isCleanupOperation(env) or isHelpRequest()): env = Platform.configurePlatform(env) - -# the various things we build. -# Each entry actually is a SCons-Node list. -# Passing these entries to other builders defines dependencies. -# 'lumiera' : the App -# 'gui' : the GTK UI (plugin) -# 'plugins' : plugin shared lib -# 'tools' : small tool applications (e.g mpegtoc) -defineBuildTargets(env) -definePostBuildTargets(env) -defineInstallTargets(env) +##################################################################### + +# define Icons to render and install +vector_icon_dir = env.subst(env.path.srcIcon+'svg') +prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') +icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + +#define Configuration files to install +config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + ) + +doxydoc = env.Doxygen('doc/devel/Doxyfile') +env.Alias ('doc', doxydoc) +env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) +# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) + + + +### === MAIN BUILD === ############################################## + +# call subdir SConscript(s) for to define the actual build targets +SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') + +# artifacts defined by the build targets +Import('lumiera plugins tools gui testsuite') + + + +# additional files to be cleaned when cleaning 'build' +env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) +env.Clean ('build', [ 'src/pre.gch' ]) + + + +### === Alias Targets === ########################################### + +build = env.Alias('build', lumiera + plugins + tools +gui) + +env.Alias ('all', build + testsuite + doxydoc) +env.Default('build') +# SCons default target + +env.Alias('install', gui) +env.Alias('install', '$DESTDIR') + +##################################################################### diff --git a/src/SConscript b/src/SConscript index c0718205d..3127af3ec 100644 --- a/src/SConscript +++ b/src/SConscript @@ -9,6 +9,7 @@ from Buildhelper import scanSubtree Import('env icons config') +# define the source file/dirs comprising each artifact to be built. lLib = env.SharedLibrary('lumiera', srcSubtree('lib'), install=True) lApp = env.SharedLibrary('lumieracommon', srcSubtree('common'), install=True, LIBS=lLib) @@ -23,6 +24,11 @@ lumiera = ( env.Program('lumiera', ['lumiera/main.cpp'] + core, install=True) + config ) +# Install the lumiera application: +# symlink the executable into the bin dir +env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') + + # building Lumiera Plugins plugins = [] # currently none From 117851a94a503f6d4fa305932b62495f944096d7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 10 Jan 2012 04:02:29 +0100 Subject: [PATCH 285/296] get rid of the additional scripts dir in pythonpath --- SConstruct | 1 - admin/{render_icon.py => scons/IconSvgRenderer.py} | 7 ++++--- admin/scons/LumieraEnvironment.py | 2 +- admin/scons/Setup.py | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) rename admin/{render_icon.py => scons/IconSvgRenderer.py} (99%) diff --git a/SConstruct b/SConstruct index e081d9144..1f30f54f9 100644 --- a/SConstruct +++ b/SConstruct @@ -30,7 +30,6 @@ #-----------------------------------Configuration TOOLDIR = './admin/scons' # SCons plugins -SCRIPTDIR = './admin' #-----------------------------------Configuration diff --git a/admin/render_icon.py b/admin/scons/IconSvgRenderer.py similarity index 99% rename from admin/render_icon.py rename to admin/scons/IconSvgRenderer.py index 63bacbf37..363b696ac 100755 --- a/admin/render_icon.py +++ b/admin/scons/IconSvgRenderer.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -# render_icons.py - Icon rendering utility script +# IconSvgRenderer.py - Icon rendering utility script # # Copyright (C) Lumiera.org # 2008, Joel Holdsworth @@ -19,11 +19,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +import os import sys import getopt -from xml.dom import minidom -import os import shutil +from xml.dom import minidom + #svgDir = "svg" #prerenderedDir = "prerendered" diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 1c314401b..100c83ca8 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -123,7 +123,7 @@ def register_LumieraResourceBuilder(env): used to generate png from the svg source using librsvg. """ - import render_icon as renderer # load Joel's python script for invoking the rsvg-convert (SVG render) + import IconSvgRenderer as renderer # load Joel's python script for invoking the rsvg-convert (SVG render) renderer.rsvgPath = env.subst("$TARGDIR/rsvg-convert") def invokeRenderer(target, source, env): diff --git a/admin/scons/Setup.py b/admin/scons/Setup.py index 23112a09b..976232198 100644 --- a/admin/scons/Setup.py +++ b/admin/scons/Setup.py @@ -32,7 +32,6 @@ TARGDIR = 'target' VERSION = '0.pre.01' TOOLDIR = './admin/scons' # SCons plugins -SCRIPTDIR = './admin' OPTCACHE = 'optcache' CUSTOPTFILE = 'custom-options' From 4d466a2c2e8450b8ca6ca7f3f0ee277889464f89 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 10 Jan 2012 05:09:32 +0100 Subject: [PATCH 286/296] reorganise the initial setup calls --- SConstruct | 7 +--- admin/scons/LumieraEnvironment.py | 14 +++++-- admin/scons/Options.py | 23 +++--------- admin/scons/Platform.py | 30 +++++---------- admin/scons/Setup.py | 61 ++++++++++++------------------- 5 files changed, 52 insertions(+), 83 deletions(-) diff --git a/SConstruct b/SConstruct index 1f30f54f9..20788c040 100644 --- a/SConstruct +++ b/SConstruct @@ -39,7 +39,6 @@ import os import sys sys.path.append(TOOLDIR) -sys.path.append(SCRIPTDIR) from Buildhelper import * from LumieraEnvironment import * @@ -52,10 +51,8 @@ import Platform ##################################################################### -env = Setup.setupBasicEnvironment() - -if not (isCleanupOperation(env) or isHelpRequest()): - env = Platform.configurePlatform(env) +env = Setup.defineBuildEnvironment() +env = Platform.configure(env) ##################################################################### diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 100c83ca8..2acb071d9 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -22,7 +22,6 @@ ##################################################################### -import os from os import path import SCons @@ -38,9 +37,15 @@ class LumieraEnvironment(Environment): This allows us to carry structured config data without using global vars. Idea inspired by Ardour. """ - def __init__(self, pathConfig, **kw): - Environment.__init__ (self,**kw) - self.path = Record (pathConfig) + def __init__(self, buildSetup, buildVars, **kw): + kw.update(VERSION = buildSetup.VERSION + ,TARGDIR = buildSetup.TARGDIR + ,DESTDIR = '$INSTALLDIR/$PREFIX' + ,toolpath = [buildSetup.TOOLDIR ] + ,variables = buildVars + ) + Environment.__init__ (self, **kw) + self.path = Record (extract_localPathDefs(buildSetup)) # e.g. buildExe -> env.path.buildExe self.libInfo = {} self.Tool("BuilderGCH") self.Tool("BuilderDoxygen") @@ -49,6 +54,7 @@ class LumieraEnvironment(Environment): register_LumieraResourceBuilder(self) register_LumieraCustomBuilders(self) + def Configure (self, *args, **kw): kw['env'] = self return apply(LumieraConfigContext, args, kw) diff --git a/admin/scons/Options.py b/admin/scons/Options.py index ed1c8bb93..bbccb20bd 100644 --- a/admin/scons/Options.py +++ b/admin/scons/Options.py @@ -22,28 +22,19 @@ ##################################################################### - -import os -import sys - -from SCons.Script import * - -from Buildhelper import * -from LumieraEnvironment import * - - -##################################################################### +from SCons.Script import PathVariable, EnumVariable, BoolVariable, Help -def defineCmdlineVariables(vars): + +def defineCmdlineVariables(buildVars): """ several toggles and configuration variables can be set on the commandline, current settings will be persisted in a options cache file. you may define custom variable settings in a separate file. Commandline will override both. """ - vars.AddVariables( + buildVars.AddVariables( ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') ,('CC', 'Set the C compiler to use.', 'gcc') ,('CXX', 'Set the C++ compiler to use.', 'g++') @@ -63,12 +54,10 @@ def defineCmdlineVariables(vars): ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) ) - - return vars -def prepareOptionsHelp(vars,env): +def prepareOptionsHelp(buildVars,env): prelude = """ USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] Build and optionally install Lumiera. @@ -86,7 +75,7 @@ Special Targets: Configuration Options: """ - Help(prelude + vars.GenerateHelpText(env)) + Help(prelude + buildVars.GenerateHelpText(env)) diff --git a/admin/scons/Platform.py b/admin/scons/Platform.py index 4d5d12e7b..4a102b040 100644 --- a/admin/scons/Platform.py +++ b/admin/scons/Platform.py @@ -22,32 +22,22 @@ ##################################################################### -# NOTE: scons -h for help. -# Read more about the SCons build system at: http://www.scons.org -# Basically, this script just /defines/ the components and how they -# fit together. SCons will derive the necessary build steps. - - - -import os -import sys - from SCons.Script import Exit - -from Buildhelper import * -from LumieraEnvironment import * +from Buildhelper import isCleanupOperation, isHelpRequest - -def configurePlatform(env): - """ locate required libs. +def configure(env): + """ locate required libraries. setup platform specific options. Abort build in case of failure. """ + if isCleanupOperation(env) or isHelpRequest(): + return env # skip configure in these cases + conf = env.Configure() - # run all configuration checks in the given env + # run all configuration checks in the build environment defined thus far # Perform checks for prerequisites -------------------------------------------- problems = [] @@ -63,11 +53,11 @@ def configurePlatform(env): if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): problems.append('Did not find the pthread lib or pthread.h.') else: - conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') - conf.env.Append(CCFLAGS = ' -pthread') + conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') + conf.env.Append(CCFLAGS = ' -pthread') if conf.CheckCHeader('execinfo.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') + conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') if conf.CheckCHeader('valgrind/valgrind.h'): conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') diff --git a/admin/scons/Setup.py b/admin/scons/Setup.py index 976232198..530ebdb91 100644 --- a/admin/scons/Setup.py +++ b/admin/scons/Setup.py @@ -21,14 +21,15 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ##################################################################### +from SCons.Script import EnsurePythonVersion, EnsureSConsVersion, Variables, Decider -# NOTE: scons -h for help. -# Read more about the SCons build system at: http://www.scons.org -# Basically, this script just /defines/ the components and how they -# fit together. SCons will derive the necessary build steps. +from LumieraEnvironment import * +from Buildhelper import * +import Options -#-----------------------------------Configuration + +#-------------------------------------------------------Configuration TARGDIR = 'target' VERSION = '0.pre.01' TOOLDIR = './admin/scons' # SCons plugins @@ -51,50 +52,33 @@ installIcon = '#$DESTDIR/share/lumiera/icons' installUIRes = '#$DESTDIR/share/lumiera/' installConf = '#$DESTDIR/lib/lumiera/config' -#-----------------------------------Configuration -localDefinitions = locals() +#-------------------------------------------------------Configuration +buildSetup = Record(locals()) -import os -import sys -from SCons.Script import * - -from Options import * - -from Buildhelper import * -from LumieraEnvironment import * - - -##################################################################### - -def setupBasicEnvironment(): - """ define cmdline options, build type decisions +def defineBuildEnvironment(): + """ create a custom build environment, + define the basic compiler and linker flags, + define locations in source and target tree, + parse the commandline and pick up options """ - EnsurePythonVersion(2,4) EnsureSConsVersion(1,0) + EnsurePythonVersion(2,4) + Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 - Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 - - vars = Variables([OPTCACHE, CUSTOPTFILE]) - vars = defineCmdlineVariables(vars) - - env = LumieraEnvironment(variables=vars - ,toolpath = [TOOLDIR] - ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe - ,TARGDIR = TARGDIR - ,DESTDIR = '$INSTALLDIR/$PREFIX' - ,VERSION = VERSION - ) - handleVerboseMessages(env) + buildVars = Variables([OPTCACHE, CUSTOPTFILE]) + Options.defineCmdlineVariables(buildVars) + env = LumieraEnvironment(buildSetup, buildVars) env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines , CCFLAGS='-Wall -Wextra ' , CFLAGS='-std=gnu99' ) + handleVerboseMessages(env) handleNoBugSwitches(env) env.Append(CPPDEFINES = '_GNU_SOURCE') @@ -110,10 +94,12 @@ def setupBasicEnvironment(): appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') - prepareOptionsHelp(vars,env) - vars.Save(OPTCACHE, env) + Options.prepareOptionsHelp(buildVars,env) + buildVars.Save(OPTCACHE, env) return env + + def appendCppDefine(env,var,cppVar, elseVal=''): if env[var]: env.Append(CPPDEFINES = env.subst(cppVar) ) @@ -139,6 +125,7 @@ def handleNoBugSwitches(env): elif level == 'RELEASE': env.Replace( DEBUG = 0 ) + def handleVerboseMessages(env): """ toggle verbose build output """ if not env['VERBOSE']: From d793a070371c38d76de3e65770c6025bfb8120ef Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 10 Jan 2012 05:15:45 +0100 Subject: [PATCH 287/296] Icon-Rendering: Inkscape is not really required the icon rendering script from Joel had a function to invoke Inkscape; it seems this was a leftover from earlier attempts to render the icons. Now, we seem to rely on lib Cairor solely --- admin/scons/IconSvgRenderer.py | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/admin/scons/IconSvgRenderer.py b/admin/scons/IconSvgRenderer.py index 363b696ac..2e50d652b 100755 --- a/admin/scons/IconSvgRenderer.py +++ b/admin/scons/IconSvgRenderer.py @@ -26,9 +26,6 @@ import shutil from xml.dom import minidom -#svgDir = "svg" -#prerenderedDir = "prerendered" -inkscapePath = "/usr/bin/inkscape" rsvgPath = "./rsvg-convert" artworkLayerPrefix = "artwork:" @@ -38,7 +35,7 @@ artworkLayerPrefix = "artwork:" # - to parse a SVG # - to invoke Inkscape to render this SVG into a raster image (icon) # -# For the actual call into Incscape we rely on an executable 'rsvg-convert', +# For the actual Cairo based SVG rendering we rely on an executable 'rsvg-convert', # which is built during the Lumiera build process. # # Judging from the code and the actual SVGs, this seems to work as follows: @@ -116,22 +113,6 @@ def parseSVG( file_path ): return artwork_name, size, parsePlateLayer( plate ) return None -def renderSvgInkscape(file_path, out_dir, artwork_name, rectangle, doc_size): - - # Calculate the rendering rectangle - x1 = rectangle[0] - y1 = doc_size[1] - rectangle[1] - rectangle[3] - x2 = x1 + rectangle[2] - y2 = y1 + rectangle[3] - - # Call Inkscape to do the render - os.spawnlp(os.P_WAIT, inkscapePath, inkscapePath, - file_path, - "-z", - "-a %g:%g:%g:%g" % (x1, y1, x2, y2), - "-w %g" % (rectangle[2]), "-h %g" % (rectangle[3]), - "--export-png=" + os.path.join(out_dir, "%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name))) - def renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size): # Prepare a Cairo context width = int(rectangle[2]) @@ -157,18 +138,6 @@ def getTargetNames(file_path): artwork_name, _ , rectangles = parseSVG(file_path) return ["%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name) for rectangle in rectangles ] -#def renderSvgIcons(): -# listing = os.listdir(svgDir) -# for file_path in listing: -# [root, extension] = os.path.splitext(file_path) -# if extension.lower() == ".svg": -# renderSvgIcon(os.path.join(svgDir, file_path)) - -#def copyPrerenderedIcons(): -# listing = os.listdir(prerenderedDir) -# for list_item in listing: -# src_dir = os.path.join(prerenderedDir, list_item) -# copyMergeDirectory(src_dir, list_item) def printHelp(): print "render-icon.py SRCFILE.svg TARGETDIR" From 27db94a64c3a4d4a7eb41bc980021f7316ab320d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 10 Jan 2012 05:52:00 +0100 Subject: [PATCH 288/296] adjust code / indentation style no functional change --- admin/scons/Buildhelper.py | 47 +++-- admin/scons/IconSvgRenderer.py | 221 ++++++++++----------- admin/scons/LumieraEnvironment.py | 68 +++---- src/tool/rsvg-convert.c | 312 ++++++++++++++++-------------- 4 files changed, 332 insertions(+), 316 deletions(-) diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 990fa5921..9460defaf 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -25,11 +25,8 @@ import os import sys import glob import fnmatch -import re -import tarfile from SCons import Util -from SCons.Action import Action @@ -65,12 +62,12 @@ def scanSubtree(roots, patterns=SRCPATTERNS): (python generator function) """ for root in globRootdirs(roots): - for (dir,_,files) in os.walk(root): - if dir.startswith('./'): - dir = dir[2:] + for (d,_,files) in os.walk(root): + if d.startswith('./'): + d = d[2:] for p in patterns: for f in fnmatch.filter(files, p): - yield os.path.join(dir,f) + yield os.path.join(d,f) @@ -78,16 +75,16 @@ def globRootdirs(roots): """ helper: expand shell wildcards and filter the resulting list, so that it only contains existing directories """ - filter = lambda f: os.path.isdir(f) and os.path.exists(f) + isDirectory = lambda f: os.path.isdir(f) and os.path.exists(f) roots = glob.glob(roots) - return (dir for dir in roots if filter(dir) ) + return (d for d in roots if isDirectory(d) ) def findSrcTrees(location, patterns=SRCPATTERNS): """ find possible source tree roots, starting with the given location. When delving down from the initial location(s), a source tree is defined - as a directory containing source files and possibly further sub directories. + as a directory containidsource files and possibly further sub directories. After having initially expanded the given location with #globRootdirs, each directory is examined depth first, until encountering a directory containing source files, which then yields a result. Especially, this can be used to traverse @@ -95,11 +92,11 @@ def findSrcTrees(location, patterns=SRCPATTERNS): to be built into packages, plugins, individual tool executables etc. @return: the relative path names of all source root dirs found (generator function). """ - for dir in globRootdirs(location): - if isSrcDir(dir,patterns): - yield dir + for directory in globRootdirs(location): + if isSrcDir (directory,patterns): + yield directory else: - for result in findSrcTrees(str(dir)+'/*'): + for result in findSrcTrees (str(directory)+'/*'): yield result @@ -109,7 +106,7 @@ def isSrcDir(path, patterns=SRCPATTERNS): @return: True if it's a directory containing any source file """ if not os.path.isdir(path): - return False + return False else: for p in patterns: if glob.glob(path+'/'+p): @@ -124,30 +121,30 @@ def filterNodes(nlist, removeName=None): if removeName: predicate = lambda n : not fnmatch.fnmatch(os.path.basename(str(n[0])), removeName) else: - predicate = lambda n : True; + predicate = lambda n : True return filter(predicate, nlist) -def getDirname(dir, basePrefix=None): +def getDirname (d, basePrefix=None): """ extract directory name without leading path, or without the explicitly given basePrefix """ - dir = os.path.realpath(dir) - if not os.path.isdir(dir): - dir,_ = os.path.split(dir) + d = os.path.realpath(d) + if not os.path.isdir(d): + d,_ = os.path.split(d) if basePrefix: basePrefix = os.path.realpath(basePrefix) - if str(dir).startswith(basePrefix): - name = str(dir)[len(basePrefix):] + if str(d).startswith(basePrefix): + name = str(d)[len(basePrefix):] else: - _, name = os.path.split(dir) + _, name = os.path.split(d) return name -def createPlugins(env, dir, **kw): +def createPlugins(env, directory, **kw): """ investigate the given source directory to identify all contained source trees. @return: a list of build nodes defining a plugin for each of these source trees. """ @@ -155,7 +152,7 @@ def createPlugins(env, dir, **kw): , srcSubtree(tree) , **kw ) - for tree in findSrcTrees(dir) + for tree in findSrcTrees(directory) ] diff --git a/admin/scons/IconSvgRenderer.py b/admin/scons/IconSvgRenderer.py index 2e50d652b..9de758100 100755 --- a/admin/scons/IconSvgRenderer.py +++ b/admin/scons/IconSvgRenderer.py @@ -54,130 +54,133 @@ artworkLayerPrefix = "artwork:" # -def createDirectory( name ): - try: - if os.path.isfile(name): - os.remove(name) - if not os.path.exists(name): - os.mkdir(name) - except: - print 'WARNING: createDirectory("%s") failed. Permission problems?' % name +def createDirectory (name): + try: + if os.path.isfile (name): + os.remove (name) + if not os.path.exists (name): + os.mkdir (name) + except: + print 'WARNING: createDirectory("%s") failed. Permission problems?' % name -def copyMergeDirectory( src, dst ): - listing = os.listdir(src) - for file_name in listing: - src_file_path = os.path.join(src, file_name) - dst_file_path = os.path.join(dst, file_name) - shutil.copyfile(src_file_path, dst_file_path) +def copyMergeDirectory (src, dst): + listing = os.listdir (src) + for file_name in listing: + src_file_path = os.path.join (src, file_name) + dst_file_path = os.path.join (dst, file_name) + shutil.copyfile (src_file_path, dst_file_path) -def getDocumentSize( svg_element ): - width = float(svg_element.getAttribute("width")) - height = float(svg_element.getAttribute("height")) - return [width, height] +def getDocumentSize (svg_element): + width = float(svg_element.getAttribute("width")) + height = float(svg_element.getAttribute("height")) + return [width, height] -def findChildLayerElement( parent_element ): - for node in parent_element.childNodes: - if node.nodeType == minidom.Node.ELEMENT_NODE: - if node.tagName == "g": - if node.getAttribute("inkscape:groupmode") == "layer": - return node - return None - -def parsePlateLayer( layer ): - rectangles = [] - for node in layer.childNodes: - if node.nodeType == minidom.Node.ELEMENT_NODE: - if node.tagName == "rect": - x = float(node.getAttribute("x")) - y = float(node.getAttribute("y")) - width = float(node.getAttribute("width")) - height = float(node.getAttribute("height")) - rectangles.append([x, y, width, height]) - return rectangles +def findChildLayerElement (parent_element): + for node in parent_element.childNodes: + if node.nodeType == minidom.Node.ELEMENT_NODE: + if node.tagName == "g": + if node.getAttribute("inkscape:groupmode") == "layer": + return node + return None -def parseSVG( file_path ): - print "Parsing " + file_path - svgdoc = minidom.parse(file_path) - for root_node in svgdoc.childNodes: - if root_node.nodeType == minidom.Node.ELEMENT_NODE: - if root_node.tagName == "svg": - size = getDocumentSize( root_node ) - layer = findChildLayerElement( root_node ) - if layer != None: - layer_name = layer.getAttribute("inkscape:label") - if layer_name[:len(artworkLayerPrefix)] == artworkLayerPrefix: - artwork_name = layer_name[len(artworkLayerPrefix):] - plate = findChildLayerElement( layer ) - if plate != None: - return artwork_name, size, parsePlateLayer( plate ) - return None +def parsePlateLayer (layer): + rectangles = [] + for node in layer.childNodes: + if node.nodeType == minidom.Node.ELEMENT_NODE: + if node.tagName == "rect": + x = float(node.getAttribute("x")) + y = float(node.getAttribute("y")) + width = float(node.getAttribute("width")) + height = float(node.getAttribute("height")) + rectangles.append([x, y, width, height]) + return rectangles -def renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size): - # Prepare a Cairo context - width = int(rectangle[2]) - height = int(rectangle[3]) - - if not os.path.exists(rsvgPath): - print "Error: executable %s not found." % rsvgPath + +def parseSVG (file_path): + print "Parsing " + file_path + svgdoc = minidom.parse (file_path) + for root_node in svgdoc.childNodes: + if root_node.nodeType == minidom.Node.ELEMENT_NODE: + if root_node.tagName == "svg": + size = getDocumentSize (root_node) + layer = findChildLayerElement (root_node) + if layer != None: + layer_name = layer.getAttribute ("inkscape:label") + if layer_name[:len(artworkLayerPrefix)] == artworkLayerPrefix: + artwork_name = layer_name[len(artworkLayerPrefix):] + plate = findChildLayerElement(layer) + if plate != None: + return artwork_name, size, parsePlateLayer(plate) + return None + + +def renderSvgRsvg (file_path, out_dir, artwork_name, rectangle, _doc_size): + # Prepare a Cairo context + width = int(rectangle[2]) + height = int(rectangle[3]) - os.spawnlp(os.P_WAIT, rsvgPath, rsvgPath, - "--source-rect=%g:%g:%g:%g" % (rectangle[0], rectangle[1], rectangle[2], rectangle[3]), - "--output=" + os.path.join(out_dir, "%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name)), - file_path) + if not os.path.exists(rsvgPath): + print "Error: executable %s not found." % rsvgPath + + os.spawnlp(os.P_WAIT, rsvgPath, rsvgPath, + "--source-rect=%g:%g:%g:%g" % (rectangle[0], rectangle[1], width, height), + "--output=" + os.path.join(out_dir, "%gx%g/%s.png" % (width, height, artwork_name)), + file_path) -def renderSvgIcon(file_path, out_dir): - artwork_name, doc_size, rectangles = parseSVG(file_path) - for rectangle in rectangles: - renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size) +def renderSvgIcon (file_path, out_dir): + artwork_name, doc_size, rectangles = parseSVG (file_path) + for rectangle in rectangles: + renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size) -def getTargetNames(file_path): - """get a list of target names to be rendered from the given source SVG - usable to setup the build targets for SCons - """ - artwork_name, _ , rectangles = parseSVG(file_path) - return ["%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name) for rectangle in rectangles ] +def getTargetNames (file_path): + """get a list of target names to be rendered from the given source SVG + usable to setup the build targets for SCons + """ + artwork_name, _ , rectangles = parseSVG (file_path) + return ["%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name) for rectangle in rectangles ] def printHelp(): - print "render-icon.py SRCFILE.svg TARGETDIR" - print "An icon rendering utility script for lumiera" + print "render-icon.py SRCFILE.svg TARGETDIR" + print "An icon rendering utility script for lumiera" def parseArguments(argv): - optlist, args = getopt.getopt(argv, "") - - if len(args) == 2: - return args[0], args[1] - - printHelp() - return None, None - -def main(argv): - in_path, out_dir = parseArguments(argv) - - if not (in_path and out_dir): - print "Missing arguments in_path and out_dir." - sys.exit(1) - - if os.path.isfile(out_dir): - print "Unable to use '%s' as output directory, because it\'s a file." % out_dir - sys.exit(1) - if not os.path.isdir(out_dir): - print "Output directory '%s' not found." % out_dir - sys.exit(1) - - # Create the icons folders - createDirectory(os.path.join(out_dir, "48x48")) - createDirectory(os.path.join(out_dir, "32x32")) - createDirectory(os.path.join(out_dir, "24x24")) - createDirectory(os.path.join(out_dir, "22x22")) - createDirectory(os.path.join(out_dir, "16x16")) - - renderSvgIcon(in_path, out_dir) + _optlist, args = getopt.getopt(argv, "") - # Copy in prerendered icons - #copyPrerenderedIcons() - + if len(args) == 2: + return args[0], args[1] + + printHelp() + return None, None + + +def main (argv): + in_path, out_dir = parseArguments(argv) + + if not (in_path and out_dir): + print "Missing arguments in_path and out_dir." + sys.exit(1) + + if os.path.isfile(out_dir): + print "Unable to use '%s' as output directory, because it\'s a file." % out_dir + sys.exit(1) + if not os.path.isdir(out_dir): + print "Output directory '%s' not found." % out_dir + sys.exit(1) + + # Create the icons folders + createDirectory(os.path.join(out_dir, "48x48")) + createDirectory(os.path.join(out_dir, "32x32")) + createDirectory(os.path.join(out_dir, "24x24")) + createDirectory(os.path.join(out_dir, "22x22")) + createDirectory(os.path.join(out_dir, "16x16")) + + renderSvgIcon (in_path, out_dir) + + + + if __name__=="__main__": main(sys.argv[1:]) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 2acb071d9..f033f4d64 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -24,8 +24,8 @@ from os import path -import SCons import SCons.SConf +from SCons.Action import Action from SCons.Environment import Environment from Buildhelper import * @@ -157,43 +157,43 @@ def register_LumieraResourceBuilder(env): return (generateTargets, source) def IconResource(env, source): - """Copy icon pixmap to corresponding icon dir. """ - subdir = getDirname(str(source)) - toBuild = env.path.buildIcon+subdir - toInstall = env.path.installIcon+subdir - env.Install (toInstall, source) - return env.Install(toBuild, source) + """Copy icon pixmap to corresponding icon dir. """ + subdir = getDirname(str(source)) + toBuild = env.path.buildIcon+subdir + toInstall = env.path.installIcon+subdir + env.Install (toInstall, source) + return env.Install(toBuild, source) def GuiResource(env, source): - subdir = getDirname(str(source)) - toBuild = env.path.buildUIRes+subdir - toInstall = env.path.installUIRes+subdir - env.Install (toInstall, source) - return env.Install(toBuild, source) + subdir = getDirname(str(source)) + toBuild = env.path.buildUIRes+subdir + toInstall = env.path.installUIRes+subdir + env.Install (toInstall, source) + return env.Install(toBuild, source) def ConfigData(env, source, targetDir=None): - """ install (copy) configuration- and metadata. - target dir is either the install location configured (in SConstruct), - or an explicitly given absolute or relative path segment, which might refer - to the location of the executable through the $ORIGIN token - """ - subdir = getDirname(str(source), env.path.srcConf) # removes source location path prefix - if targetDir: - if path.isabs(targetDir): - toBuild = toInstall = path.join(targetDir,subdir) - else: - if targetDir.startswith('$ORIGIN'): - targetDir = targetDir[len('$ORIGIN'):] - toBuild = path.join(env.path.buildExe, targetDir, subdir) - toInstall = path.join(env.path.installExe, targetDir, subdir) - else: - toBuild = path.join(env.path.buildConf, targetDir, subdir) - toInstall = path.join(env.path.installConf, targetDir, subdir) - else: - toBuild = path.join(env.path.buildConf,subdir) - toInstall = path.join(env.path.installConf,subdir) - env.Install (toInstall, source) - return env.Install(toBuild, source) + """ install (copy) configuration- and metadata. + target dir is either the install location configured (in SConstruct), + or an explicitly given absolute or relative path segment, which might refer + to the location of the executable through the $ORIGIN token + """ + subdir = getDirname(str(source), env.path.srcConf) # removes source location path prefix + if targetDir: + if path.isabs(targetDir): + toBuild = toInstall = path.join(targetDir,subdir) + else: + if targetDir.startswith('$ORIGIN'): + targetDir = targetDir[len('$ORIGIN'):] + toBuild = path.join(env.path.buildExe, targetDir, subdir) + toInstall = path.join(env.path.installExe, targetDir, subdir) + else: + toBuild = path.join(env.path.buildConf, targetDir, subdir) + toInstall = path.join(env.path.installConf, targetDir, subdir) + else: + toBuild = path.join(env.path.buildConf,subdir) + toInstall = path.join(env.path.installConf,subdir) + env.Install (toInstall, source) + return env.Install(toBuild, source) buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS") diff --git a/src/tool/rsvg-convert.c b/src/tool/rsvg-convert.c index 9fcf67b9c..eee28cc07 100644 --- a/src/tool/rsvg-convert.c +++ b/src/tool/rsvg-convert.c @@ -1,32 +1,28 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- +/* + rsvg-convert.c - Command line utility for exercising rsvg with cairo. + + Copyright (C) Red Hat, Inc. + 2005, Carl Worth + Caleb Moore + Dom Lachowicz + 2008, Joel Holdsworth + + 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. + +* *****************************************************/ - rsvg-convert.c: Command line utility for exercising rsvg with cairo. - - Copyright (C) 2005 Red Hat, Inc. - Copyright (C) 2005 Dom Lachowicz - Copyright (C) 2005 Caleb Moore - Copyright (C) 2008 Joel Holdsworth - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU Library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - - Authors: Carl Worth , - Caleb Moore , - Dom Lachowicz , - Joel Holdsworth -*/ #ifndef N_ #define N_(X) X @@ -40,183 +36,203 @@ #include #include + #ifdef CAIRO_HAS_PS_SURFACE -#include + #include #endif #ifdef CAIRO_HAS_PDF_SURFACE -#include + #include #endif #ifdef CAIRO_HAS_SVG_SURFACE -#include + #include #endif #ifndef _ -#define _(X) X + #define _(X) X #endif -struct RsvgSizeCallbackData { + +struct RsvgSizeCallbackData + { gint width; gint height; -}; + }; -struct RsvgSourceRectangle { +struct RsvgSourceRectangle + { double left; double top; double width; double height; -}; + }; + static void display_error (GError * err) { - if (err) { - g_print ("%s\n", err->message); - g_error_free (err); + if (err) + { + g_print ("%s\n", err->message); + g_error_free (err); } } + static void rsvg_cairo_size_callback (int *width, int *height, gpointer data) { - RsvgDimensionData *dimensions = data; - *width = dimensions->width; - *height = dimensions->height; + RsvgDimensionData *dimensions = data; + *width = dimensions->width; + *height = dimensions->height; } + static cairo_status_t rsvg_cairo_write_func (void *closure, const unsigned char *data, unsigned int length) { - if (fwrite (data, 1, length, (FILE *) closure) == length) - return CAIRO_STATUS_SUCCESS; - return CAIRO_STATUS_WRITE_ERROR; + if (fwrite (data, 1, length, (FILE *) closure) == length) + return CAIRO_STATUS_SUCCESS; + return CAIRO_STATUS_WRITE_ERROR; } + int main (int argc, char **argv) { - GOptionContext *g_option_context; - int width = -1; - int height = -1; - char *source_rect_string = NULL; - char *output = NULL; - GError *error = NULL; - char *filename = NULL; - - char **args = NULL; - RsvgHandle *rsvg; - cairo_surface_t *surface = NULL; - cairo_t *cr = NULL; - RsvgDimensionData dimensions; - FILE *output_file = stdout; - - struct RsvgSourceRectangle source_rect = {0, 0, 0, 0}; + GOptionContext *g_option_context; + int width = -1; + int height = -1; + char *source_rect_string = NULL; + char *output = NULL; + GError *error = NULL; + char *filename = NULL; + + char **args = NULL; + RsvgHandle *rsvg; + cairo_surface_t *surface = NULL; + cairo_t *cr = NULL; + RsvgDimensionData dimensions; + FILE *output_file = stdout; + + struct RsvgSourceRectangle source_rect = {0, 0, 0, 0}; - GOptionEntry options_table[] = { - {"width", 'w', 0, G_OPTION_ARG_INT, &width, - N_("width [optional; defaults to the SVG's width]"), N_("")}, - {"height", 'h', 0, G_OPTION_ARG_INT, &height, - N_("height [optional; defaults to the SVG's height]"), N_("")}, - {"source-rect", 'r', 0, G_OPTION_ARG_STRING, &source_rect_string, - N_("source rectangle [optional; defaults to rectangle of the SVG document]"), N_("left:top:width:height")}, - {"output", 'o', 0, G_OPTION_ARG_STRING, &output, - N_("output filename"), NULL}, - {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("FILE")}, - {NULL} - }; + GOptionEntry options_table[] = { + {"width", 'w', 0, G_OPTION_ARG_INT, &width, + N_("width [optional; defaults to the SVG's width]"), N_("")}, + {"height", 'h', 0, G_OPTION_ARG_INT, &height, + N_("height [optional; defaults to the SVG's height]"), N_("")}, + {"source-rect", 'r', 0, G_OPTION_ARG_STRING, &source_rect_string, + N_("source rectangle [optional; defaults to rectangle of the SVG document]"), N_("left:top:width:height")}, + {"output", 'o', 0, G_OPTION_ARG_STRING, &output, + N_("output filename"), NULL}, + {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("FILE")}, + {NULL} + }; - /* Set the locale so that UTF-8 filenames work */ - setlocale(LC_ALL, ""); + /* Set the locale so that UTF-8 filenames work */ + setlocale (LC_ALL, ""); - g_option_context = g_option_context_new (_("- SVG Converter")); - g_option_context_add_main_entries (g_option_context, options_table, NULL); - g_option_context_set_help_enabled (g_option_context, TRUE); - if (!g_option_context_parse (g_option_context, &argc, &argv, &error)) { - display_error (error); - exit (1); + g_option_context = g_option_context_new (_("- SVG Converter")); + g_option_context_add_main_entries (g_option_context, options_table, NULL); + g_option_context_set_help_enabled (g_option_context, TRUE); + if (!g_option_context_parse (g_option_context, &argc, &argv, &error)) + { + display_error (error); + exit (1); } - g_option_context_free (g_option_context); + g_option_context_free (g_option_context); - if (output != NULL) { - output_file = fopen (output, "wb"); - if (!output_file) { - fprintf (stderr, _("Error saving to file: %s\n"), output); - exit (1); + if (output != NULL) + { + output_file = fopen (output, "wb"); + if (!output_file) + { + fprintf (stderr, _("Error saving to file: %s\n"), output); + exit (1); } } - - if (args[0] != NULL) { - filename = args[0]; - } - - /* Parse the source rect */ - if(source_rect_string != NULL) { - const int n = sscanf(source_rect_string, "%lg:%lg:%lg:%lg", - &source_rect.left, &source_rect.top, - &source_rect.width, &source_rect.height); - if(n != 4 || source_rect.width <= 0.0 || source_rect.height < 0.0) { - fprintf (stderr, _("Invalid source rect: %s\n"), source_rect_string); - exit(1); - } - } - - rsvg_init (); - - rsvg = rsvg_handle_new_from_file (filename, &error); - - if (!rsvg) { - fprintf (stderr, _("Error reading SVG:")); - display_error (error); - fprintf (stderr, "\n"); - exit (1); + + if (args[0] != NULL) + filename = args[0]; + + /* Parse the source rect */ + if(source_rect_string != NULL) + { + const int n = sscanf(source_rect_string, "%lg:%lg:%lg:%lg", + &source_rect.left, &source_rect.top, + &source_rect.width, &source_rect.height); + if (n != 4 || source_rect.width <= 0.0 || source_rect.height < 0.0) + { + fprintf (stderr, _("Invalid source rect: %s\n"), source_rect_string); + exit(1); + } } - /* if the user did not specify a source rectangle, get the page size from the SVG */ - if(source_rect_string == NULL) { - rsvg_handle_set_size_callback (rsvg, rsvg_cairo_size_callback, &dimensions, NULL); - source_rect.left = 0; - source_rect.top = 0; - source_rect.width = dimensions.width; - source_rect.height = dimensions.height; + rsvg_init (); + + rsvg = rsvg_handle_new_from_file (filename, &error); + + if (!rsvg) + { + fprintf (stderr, _("Error reading SVG:")); + display_error (error); + fprintf (stderr, "\n"); + exit (1); } - rsvg_handle_get_dimensions (rsvg, &dimensions); - - if(width != -1 && height != -1) { - dimensions.width = width; - dimensions.height = height; - } else if(source_rect_string != NULL) { - dimensions.width = source_rect.width; - dimensions.height = source_rect.height; - } - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - dimensions.width, dimensions.height); - - cr = cairo_create (surface); - - cairo_translate(cr, -source_rect.left, -source_rect.top); - - if(width != -1 && height != -1 && source_rect_string != NULL) { - cairo_scale(cr, (double)dimensions.width / (double)source_rect.width, - (double)dimensions.height / (double)source_rect.height); + /* if the user did not specify a source rectangle, get the page size from the SVG */ + if(source_rect_string == NULL) + { + rsvg_handle_set_size_callback (rsvg, rsvg_cairo_size_callback, &dimensions, NULL); + source_rect.left = 0; + source_rect.top = 0; + source_rect.width = dimensions.width; + source_rect.height = dimensions.height; } - rsvg_handle_render_cairo (rsvg, cr); + rsvg_handle_get_dimensions (rsvg, &dimensions); - cairo_surface_write_to_png_stream (surface, rsvg_cairo_write_func, output_file); + if (width != -1 && height != -1) + { + dimensions.width = width; + dimensions.height = height; + } + else if(source_rect_string != NULL) + { + dimensions.width = source_rect.width; + dimensions.height = source_rect.height; + } + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + dimensions.width, + dimensions.height); - g_object_unref (G_OBJECT (rsvg)); + cr = cairo_create (surface); + + cairo_translate (cr, -source_rect.left, -source_rect.top); + + if (width != -1 && height != -1 && source_rect_string != NULL) + { + cairo_scale (cr, + (double)dimensions.width / (double)source_rect.width, + (double)dimensions.height / (double)source_rect.height); + } - cairo_destroy (cr); - cairo_surface_destroy (surface); + rsvg_handle_render_cairo (rsvg, cr); - fclose (output_file); + cairo_surface_write_to_png_stream (surface, rsvg_cairo_write_func, output_file); - rsvg_term (); + g_object_unref (G_OBJECT (rsvg)); - return 0; + cairo_destroy (cr); + cairo_surface_destroy (surface); + + fclose (output_file); + + rsvg_term (); + + return 0; } - From 56ac1afe18384579ffdd5bb9e0bf12329be4c042 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 10 Jan 2012 08:06:09 +0100 Subject: [PATCH 289/296] move Icon building down into separate SConscript --- SConstruct | 14 +-- admin/scons/Buildhelper.py | 3 +- admin/scons/LumieraEnvironment.py | 5 +- admin/scons/Setup.py | 2 - data/SConscript | 25 +++++ data/icons/README | 26 +++++ .../icons}/prerendered/16x16/panel-assets.png | Bin .../prerendered/16x16/panel-timeline.png | Bin .../icons}/prerendered/16x16/panel-viewer.png | Bin .../icons}/prerendered/22x22/panel-assets.png | Bin .../icons}/prerendered/22x22/panel-viewer.png | Bin .../icons}/prerendered/32x32/panel-assets.png | Bin .../icons}/prerendered/32x32/panel-viewer.png | Bin {icons => data/icons}/svg/app-icon.svg | 0 {icons => data/icons}/svg/tool-arrow.svg | 0 {icons => data/icons}/svg/tool-i-beam.svg | 0 {icons => data/icons}/svg/track-disabled.svg | 0 {icons => data/icons}/svg/track-enabled.svg | 0 {icons => data/icons}/svg/track-locked.svg | 0 {icons => data/icons}/svg/track-unlocked.svg | 0 {icons => data/icons}/timeline-panel.svg | 0 icons/Makefile.am | 97 ------------------ src/SConscript | 2 +- 23 files changed, 58 insertions(+), 116 deletions(-) create mode 100644 data/SConscript create mode 100644 data/icons/README rename {icons => data/icons}/prerendered/16x16/panel-assets.png (100%) rename {icons => data/icons}/prerendered/16x16/panel-timeline.png (100%) rename {icons => data/icons}/prerendered/16x16/panel-viewer.png (100%) rename {icons => data/icons}/prerendered/22x22/panel-assets.png (100%) rename {icons => data/icons}/prerendered/22x22/panel-viewer.png (100%) rename {icons => data/icons}/prerendered/32x32/panel-assets.png (100%) rename {icons => data/icons}/prerendered/32x32/panel-viewer.png (100%) rename {icons => data/icons}/svg/app-icon.svg (100%) rename {icons => data/icons}/svg/tool-arrow.svg (100%) rename {icons => data/icons}/svg/tool-i-beam.svg (100%) rename {icons => data/icons}/svg/track-disabled.svg (100%) rename {icons => data/icons}/svg/track-enabled.svg (100%) rename {icons => data/icons}/svg/track-locked.svg (100%) rename {icons => data/icons}/svg/track-unlocked.svg (100%) rename {icons => data/icons}/timeline-panel.svg (100%) delete mode 100644 icons/Makefile.am diff --git a/SConstruct b/SConstruct index 20788c040..a82c9060f 100644 --- a/SConstruct +++ b/SConstruct @@ -57,18 +57,6 @@ env = Platform.configure(env) ##################################################################### -# define Icons to render and install -vector_icon_dir = env.subst(env.path.srcIcon+'svg') -prerendered_icon_dir = env.subst(env.path.srcIcon+'prerendered') -icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] - + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) - -#define Configuration files to install -config = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) - doxydoc = env.Doxygen('doc/devel/Doxyfile') env.Alias ('doc', doxydoc) env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) @@ -79,7 +67,7 @@ env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) ### === MAIN BUILD === ############################################## # call subdir SConscript(s) for to define the actual build targets -SConscript(dirs=['src','src/tool','research','tests'], exports='env icons config') +SConscript(dirs=['data','src','src/tool','research','tests'], exports='env') # artifacts defined by the build targets Import('lumiera plugins tools gui testsuite') diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 9460defaf..673d050ba 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -136,8 +136,9 @@ def getDirname (d, basePrefix=None): d,_ = os.path.split(d) if basePrefix: basePrefix = os.path.realpath(basePrefix) + name = str(d) if str(d).startswith(basePrefix): - name = str(d)[len(basePrefix):] + name = name[len(basePrefix):] else: _, name = os.path.split(d) return name diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index f033f4d64..8d2d8db43 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -171,13 +171,14 @@ def register_LumieraResourceBuilder(env): env.Install (toInstall, source) return env.Install(toBuild, source) - def ConfigData(env, source, targetDir=None): + def ConfigData(env, prefix, source, targetDir=None): """ install (copy) configuration- and metadata. target dir is either the install location configured (in SConstruct), or an explicitly given absolute or relative path segment, which might refer to the location of the executable through the $ORIGIN token """ - subdir = getDirname(str(source), env.path.srcConf) # removes source location path prefix + source = path.join(prefix,str(source)) + subdir = getDirname(source, prefix) # removes source location path prefix if targetDir: if path.isabs(targetDir): toBuild = toInstall = path.join(targetDir,subdir) diff --git a/admin/scons/Setup.py b/admin/scons/Setup.py index 530ebdb91..5a064381d 100644 --- a/admin/scons/Setup.py +++ b/admin/scons/Setup.py @@ -37,8 +37,6 @@ OPTCACHE = 'optcache' CUSTOPTFILE = 'custom-options' # these are accessible via env.path.xxxx -srcIcon = 'icons' -srcConf = 'data/config' buildExe = '#$TARGDIR' buildLib = '#$TARGDIR/modules' buildPlug = '#$TARGDIR/modules' diff --git a/data/SConscript b/data/SConscript new file mode 100644 index 000000000..d2407b502 --- /dev/null +++ b/data/SConscript @@ -0,0 +1,25 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for Icons and Resources +## + +from Buildhelper import scanSubtree + +Import('env') + + +# define Icons to render and install +vector_icon_dir = 'icons/svg' +prerendered_icon_dir = 'icons/prerendered' + +icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + +#define Configuration files to install (dir-prefix, name) +config = ( env.ConfigData('config','setup.ini', targetDir='$ORIGIN') + + env.ConfigData('config','dummy_lumiera.ini') + ) + + +Export('icons config') diff --git a/data/icons/README b/data/icons/README new file mode 100644 index 000000000..b56ffe9f2 --- /dev/null +++ b/data/icons/README @@ -0,0 +1,26 @@ +# +# Lumiera Icon Artwork +# +# Copyright (C) Lumiera.org +# 2008, Joel Holdsworth +# +# Icon Artwork and similar materials accompanying the Lumiera Application +# is dual-licensed under the GNU General Public License version 2 or above, +# and +# Creative Commons Attribution-ShareAlike 3.0 Unported License +# +# + + +This directory holds Icons and similar graphics resources for the Lumiera GUI. + +- prerendered: Raster image Icons rendered into a selection of sizes +- svg: Scalable Vector Grahpics Icons, to be rendered into suitable sizes + by the build process. Rendering is done with the help of lib Cairo. + The build creates an executable from src/tools/rsvg-convert.c + The invocation of the icon rendering is done with the help of a + Python script IconSvgRenderer.py, which first parses the SVG document + and then invokes rsvg-convert to generate the raster images. + + + diff --git a/icons/prerendered/16x16/panel-assets.png b/data/icons/prerendered/16x16/panel-assets.png similarity index 100% rename from icons/prerendered/16x16/panel-assets.png rename to data/icons/prerendered/16x16/panel-assets.png diff --git a/icons/prerendered/16x16/panel-timeline.png b/data/icons/prerendered/16x16/panel-timeline.png similarity index 100% rename from icons/prerendered/16x16/panel-timeline.png rename to data/icons/prerendered/16x16/panel-timeline.png diff --git a/icons/prerendered/16x16/panel-viewer.png b/data/icons/prerendered/16x16/panel-viewer.png similarity index 100% rename from icons/prerendered/16x16/panel-viewer.png rename to data/icons/prerendered/16x16/panel-viewer.png diff --git a/icons/prerendered/22x22/panel-assets.png b/data/icons/prerendered/22x22/panel-assets.png similarity index 100% rename from icons/prerendered/22x22/panel-assets.png rename to data/icons/prerendered/22x22/panel-assets.png diff --git a/icons/prerendered/22x22/panel-viewer.png b/data/icons/prerendered/22x22/panel-viewer.png similarity index 100% rename from icons/prerendered/22x22/panel-viewer.png rename to data/icons/prerendered/22x22/panel-viewer.png diff --git a/icons/prerendered/32x32/panel-assets.png b/data/icons/prerendered/32x32/panel-assets.png similarity index 100% rename from icons/prerendered/32x32/panel-assets.png rename to data/icons/prerendered/32x32/panel-assets.png diff --git a/icons/prerendered/32x32/panel-viewer.png b/data/icons/prerendered/32x32/panel-viewer.png similarity index 100% rename from icons/prerendered/32x32/panel-viewer.png rename to data/icons/prerendered/32x32/panel-viewer.png diff --git a/icons/svg/app-icon.svg b/data/icons/svg/app-icon.svg similarity index 100% rename from icons/svg/app-icon.svg rename to data/icons/svg/app-icon.svg diff --git a/icons/svg/tool-arrow.svg b/data/icons/svg/tool-arrow.svg similarity index 100% rename from icons/svg/tool-arrow.svg rename to data/icons/svg/tool-arrow.svg diff --git a/icons/svg/tool-i-beam.svg b/data/icons/svg/tool-i-beam.svg similarity index 100% rename from icons/svg/tool-i-beam.svg rename to data/icons/svg/tool-i-beam.svg diff --git a/icons/svg/track-disabled.svg b/data/icons/svg/track-disabled.svg similarity index 100% rename from icons/svg/track-disabled.svg rename to data/icons/svg/track-disabled.svg diff --git a/icons/svg/track-enabled.svg b/data/icons/svg/track-enabled.svg similarity index 100% rename from icons/svg/track-enabled.svg rename to data/icons/svg/track-enabled.svg diff --git a/icons/svg/track-locked.svg b/data/icons/svg/track-locked.svg similarity index 100% rename from icons/svg/track-locked.svg rename to data/icons/svg/track-locked.svg diff --git a/icons/svg/track-unlocked.svg b/data/icons/svg/track-unlocked.svg similarity index 100% rename from icons/svg/track-unlocked.svg rename to data/icons/svg/track-unlocked.svg diff --git a/icons/timeline-panel.svg b/data/icons/timeline-panel.svg similarity index 100% rename from icons/timeline-panel.svg rename to data/icons/timeline-panel.svg diff --git a/icons/Makefile.am b/icons/Makefile.am deleted file mode 100644 index 0f34d924b..000000000 --- a/icons/Makefile.am +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (C) Lumiera.org -# 2008, Joel Holdsworth -# -# 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. - -svgdir = $(top_srcdir)/icons/svg -prerendereddir = $(top_srcdir)/icons/prerendered -icondir = $(top_builddir) -iconcommand = python $(top_srcdir)/admin/render_icon.py - -16x16 = $(icondir)/16x16 -22x22 = $(icondir)/22x22 -24x24 = $(icondir)/24x24 -32x32 = $(icondir)/32x32 -48x48 = $(icondir)/48x48 - -16x16pre = $(prerendereddir)/16x16 -22x22pre = $(prerendereddir)/22x22 -24x24pre = $(prerendereddir)/24x24 -32x32pre = $(prerendereddir)/32x32 -48x48pre = $(prerendereddir)/48x48 - -icons = \ - $(16x16)/app-icon.png $(22x22)/app-icon.png $(24x24)/app-icon.png $(32x32)/app-icon.png $(48x48)/app-icon.png \ - $(16x16)/tool-arrow.png $(22x22)/tool-arrow.png $(24x24)/tool-arrow.png $(32x32)/tool-arrow.png $(48x48)/tool-arrow.png \ - $(16x16)/tool-i-beam.png $(22x22)/tool-i-beam.png $(24x24)/tool-i-beam.png $(32x32)/tool-i-beam.png $(48x48)/tool-i-beam.png \ - $(16x16)/track-disabled.png \ - $(16x16)/track-enabled.png \ - $(16x16)/track-locked.png \ - $(16x16)/track-unlocked.png \ - $(16x16)/panel-assets.png $(22x22)/panel-assets.png $(32x32)/panel-assets.png \ - $(16x16)/panel-timeline.png \ - $(16x16)/panel-viewer.png $(22x22)/panel-viewer.png $(32x32)/panel-viewer.png - -dist_pkgdata_DATA += $(icons) - -all: $(icons) - -clean-local: - rm -rf $(16x16) $(22x22) $(24x24) $(32x32) $(48x48) - -# ========== SVG Icons ========== - -# App Icon - -$(16x16)/app-icon.png $(22x22)/app-icon.png $(24x24)/app-icon.png $(32x32)/app-icon.png $(48x48)/app-icon.png : $(svgdir)/app-icon.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) - -# Timeline Tools - -$(16x16)/tool-arrow.png $(22x22)/tool-arrow.png $(24x24)/tool-arrow.png $(32x32)/tool-arrow.png $(48x48)/tool-arrow.png : $(svgdir)/tool-arrow.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) -$(16x16)/tool-i-beam.png $(22x22)/tool-i-beam.png $(24x24)/tool-i-beam.png $(32x32)/tool-i-beam.png $(48x48)/tool-i-beam.png : $(svgdir)/tool-i-beam.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) - -# Timeline Tracks -$(16x16)/track-disabled.png : $(svgdir)/track-disabled.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) -$(16x16)/track-enabled.png : $(svgdir)/track-enabled.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) -$(16x16)/track-locked.png : $(svgdir)/track-locked.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) -$(16x16)/track-unlocked.png : $(svgdir)/track-unlocked.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) - -# ========== Prerendered Icons ========== - -# Panels - -$(16x16)/panel-assets.png: $(16x16pre)/panel-assets.png - cp $(16x16pre)/panel-assets.png $(16x16) -$(22x22)/panel-assets.png: $(22x22pre)/panel-assets.png - cp $(22x22pre)/panel-assets.png $(22x22) -$(32x32)/panel-assets.png: $(32x32pre)/panel-assets.png - cp $(32x32pre)/panel-assets.png $(32x32) - -$(16x16)/panel-timeline.png: $(16x16pre)/panel-timeline.png - cp $(16x16pre)/panel-timeline.png $(16x16) - -$(16x16)/panel-viewer.png: $(16x16pre)/panel-viewer.png - cp $(16x16pre)/panel-viewer.png $(16x16) -$(22x22)/panel-viewer.png: $(22x22pre)/panel-viewer.png - cp $(22x22pre)/panel-viewer.png $(22x22) -$(32x32)/panel-viewer.png: $(32x32pre)/panel-viewer.png - cp $(32x32pre)/panel-viewer.png $(32x32) diff --git a/src/SConscript b/src/SConscript index 3127af3ec..de4d03a91 100644 --- a/src/SConscript +++ b/src/SConscript @@ -26,7 +26,7 @@ lumiera = ( env.Program('lumiera', ['lumiera/main.cpp'] + core, install=True) # Install the lumiera application: # symlink the executable into the bin dir -env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') +env.SymLink('#$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') # building Lumiera Plugins From feebd05cbacbf367596adba3bbfda6f5d524661a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 10 Jan 2012 08:21:27 +0100 Subject: [PATCH 290/296] create separate SConscript for documentation currently just featuring Doxygen --- SConstruct | 14 ++++---------- doc/SConscript | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 doc/SConscript diff --git a/SConstruct b/SConstruct index a82c9060f..26ecf132f 100644 --- a/SConstruct +++ b/SConstruct @@ -55,22 +55,14 @@ env = Setup.defineBuildEnvironment() env = Platform.configure(env) -##################################################################### - -doxydoc = env.Doxygen('doc/devel/Doxyfile') -env.Alias ('doc', doxydoc) -env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) -# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=doxydoc) - - ### === MAIN BUILD === ############################################## # call subdir SConscript(s) for to define the actual build targets -SConscript(dirs=['data','src','src/tool','research','tests'], exports='env') +SConscript(dirs=['data','src','src/tool','research','tests','doc'], exports='env') # artifacts defined by the build targets -Import('lumiera plugins tools gui testsuite') +Import('lumiera plugins tools gui testsuite doxydoc') @@ -84,6 +76,8 @@ env.Clean ('build', [ 'src/pre.gch' ]) build = env.Alias('build', lumiera + plugins + tools +gui) +env.Alias ('doc', doxydoc) + env.Alias ('all', build + testsuite + doxydoc) env.Default('build') # SCons default target diff --git a/doc/SConscript b/doc/SConscript new file mode 100644 index 000000000..60c0964ab --- /dev/null +++ b/doc/SConscript @@ -0,0 +1,17 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for Documentation +## + +from Buildhelper import scanSubtree + +Import('env') + + +doxydoc = env.Doxygen('devel/Doxyfile') + +# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=documentation) +env.Clean (doxydoc, doxydoc + ['devel/,doxylog','devel/warnings.txt']) + + +Export('doxydoc') From 98717915b26af77df16a614dd4ea10dd37ffd47c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 10 Jan 2012 08:37:01 +0100 Subject: [PATCH 291/296] clean up top level SConstruct --- SConstruct | 44 ++++++++++++++++++++------------------------ tests/SConscript | 2 +- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/SConstruct b/SConstruct index 26ecf132f..d2468b374 100644 --- a/SConstruct +++ b/SConstruct @@ -23,46 +23,39 @@ # NOTE: scons -h for help. +# This script /defines/ the components and how they fit together. +# SCons will derive dependencies and the necessary build steps. # Read more about the SCons build system at: http://www.scons.org -# Basically, this script just /defines/ the components and how they -# fit together. SCons will derive the necessary build steps. -#-----------------------------------Configuration -TOOLDIR = './admin/scons' # SCons plugins -#-----------------------------------Configuration - - - - -import os +# SCons plugins and extension modules +#------------------------------------------------ import sys - -sys.path.append(TOOLDIR) - -from Buildhelper import * -from LumieraEnvironment import * +sys.path.append('./admin/scons') +#------------------------------------------------ import Setup import Options import Platform +from Buildhelper import * +from LumieraEnvironment import * + + ##################################################################### -env = Setup.defineBuildEnvironment() -env = Platform.configure(env) +env = Setup.defineBuildEnvironment() # dirs & compiler flags +env = Platform.configure(env) # library dependencies ### === MAIN BUILD === ############################################## -# call subdir SConscript(s) for to define the actual build targets +# call subdir SConscript(s) to define the actual build targets... SConscript(dirs=['data','src','src/tool','research','tests','doc'], exports='env') -# artifacts defined by the build targets -Import('lumiera plugins tools gui testsuite doxydoc') @@ -74,14 +67,17 @@ env.Clean ('build', [ 'src/pre.gch' ]) ### === Alias Targets === ########################################### +# pick up the targets defined by the sub SConscripts +Import('lumiera plugins tools gui testsuite doxydoc') + build = env.Alias('build', lumiera + plugins + tools +gui) - -env.Alias ('doc', doxydoc) - -env.Alias ('all', build + testsuite + doxydoc) env.Default('build') # SCons default target + +env.Alias ('all', build + testsuite + doxydoc) +env.Alias ('doc', doxydoc) + env.Alias('install', gui) env.Alias('install', '$DESTDIR') diff --git a/tests/SConscript b/tests/SConscript index 12672cd79..9c5d5c786 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -50,7 +50,7 @@ def testCollection(env,dir): -# but have to treat some subdirs individually. +# have to treat some subdirs individually. specials = ['plugin','lib','components'] moduledirs = globRootdirs('*') From 29394345af5d16bc87903a2a131ba96e2ea06b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odin=20Omdal=20H=C3=B8rthe?= Date: Tue, 13 Sep 2011 02:42:23 +0200 Subject: [PATCH 292/296] ALSA audio output experiment I think it's smart to rather use ALSA directly instead of PortAudio. ALSA is push AFAIK, and talking about it here at the hackspace, seems like the better choice. It's a bit lower level, but anyway everything speaks ALSA anyway. It's not like there's any reason to use PortAudio at all. It's just an extra abstraction. Coding for ALSA it'll also work with Pulseaudio and esd. Do people really use other sound systems than Pulseaudio, esd or plain ALSA? I can't think of it. I really the idea about building a small tool first. I'll do that. Also thought about making a small blikning cursor/text output, and syncing a BEEP-sound to that, so that I can test around with throwing in lots and lots of latency between "me" and the video, and try to sync it anyway. I should be able to read back from the sound card (or pulse audio underneath, it will just work with alsa as the abstraction) how long it takes for the bytes I'm pushing to reach the speakers, and do some buffer tuning on that. --- src/tool/alsa.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++ src/tool/main.c | 27 +++++++++ 2 files changed, 175 insertions(+) create mode 100644 src/tool/alsa.c create mode 100644 src/tool/main.c diff --git a/src/tool/alsa.c b/src/tool/alsa.c new file mode 100644 index 000000000..e6c4afbba --- /dev/null +++ b/src/tool/alsa.c @@ -0,0 +1,148 @@ +#include +#include +#include + +#include + +static snd_pcm_t* playback_handle = 0; +static snd_pcm_sw_params_t* sw_params = 0; +static snd_pcm_hw_params_t* hw_params = 0; +static snd_pcm_sframes_t buffer_size = 0; + +static snd_pcm_sframes_t written = 0; +static snd_pcm_sframes_t delay = 0; + +static unsigned int rate = 44100; + +static int audio_initialized = 0; + +size_t +audio_offset() +{ + snd_pcm_delay(playback_handle, &delay); + + return written - delay; +} + +void +audio_init() +{ + unsigned int buffer_time = 50000; + const char* device; + int err; + + if(audio_initialized) + return; + + audio_initialized = 1; + + device = getenv("ALSA_DEVICE"); + + if(!device) + device = "default"; + + if(0 > (err = snd_pcm_open(&playback_handle, device, + SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/))) + errx(EXIT_FAILURE, "Audio: Cannot open device %s: %s", device, snd_strerror(err)); + + if(0 > (err = snd_pcm_sw_params_malloc(&sw_params))) + errx(EXIT_FAILURE, "Audio: Could not allocate software parameter structure: %s", + snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_malloc(&hw_params))) + errx(EXIT_FAILURE, "Audio: Could not allocate hardware parameter structure: %s", + snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_any(playback_handle, hw_params))) + errx(EXIT_FAILURE, "Audio: Could not initializa hardware parameters: %s", + snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_set_access(playback_handle, hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED))) + errx(EXIT_FAILURE, "Audio: Could not set access type: %s", snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_set_format(playback_handle, hw_params, + SND_PCM_FORMAT_S16))) + errx(EXIT_FAILURE, "Audio: Could not set sample format to signed 16 bit " + "native endian: %s", snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, + &rate, 0))) + errx(EXIT_FAILURE, "Audio: Could not set sample rate %uHz: %s", rate, + snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2))) + errx(EXIT_FAILURE, "Audio: Could not set channel count to %u: %s", + 2, snd_strerror(err)); + + snd_pcm_hw_params_set_buffer_time_near(playback_handle, hw_params, &buffer_time, 0); + + if(0 > (err = snd_pcm_hw_params(playback_handle, hw_params))) + errx(EXIT_FAILURE, "Audio: Could not set hardware parameters: %s", snd_strerror(err)); + + fprintf(stderr, "Buffer time is %.3f seconds\n", buffer_time / 1.0e6); + + if(0 > (err = snd_pcm_sw_params_current(playback_handle, sw_params))) + errx(EXIT_FAILURE, "Audio: Could not initialize software parameters: %s", + snd_strerror(err)); + + snd_pcm_sw_params_set_start_threshold(playback_handle, sw_params, 0); + snd_pcm_sw_params_set_avail_min(playback_handle, sw_params, 1024); + + snd_pcm_uframes_t min; + snd_pcm_sw_params_get_avail_min(sw_params, &min); + fprintf(stderr, "Minimum %u\n", (unsigned) min); + + if(0 > (err = snd_pcm_sw_params(playback_handle, sw_params))) + errx(EXIT_FAILURE, "Audio: Could not set software parameters: %s", + snd_strerror(err)); + + buffer_size = snd_pcm_avail_update(playback_handle); +} + +size_t +audio_write(const void* data, size_t amount) +{ + int err; + + amount /= 4; + + for(;;) + { + err = snd_pcm_writei(playback_handle, data, amount); + + if(err == -EAGAIN) + return 0; + + if(err < 0) + { + err = snd_pcm_recover(playback_handle, err, 0); + + if(err < 0) + errx(EXIT_FAILURE, "Audio playback failed: %s", strerror(-err)); + } + + + break; + } + + written += err; + + err *= 4; + + return err; +} + +void +audio_start(unsigned int rate, unsigned int channel_count) +{ + audio_init(); + + snd_pcm_prepare(playback_handle); +} + +void +audio_stop() +{ + snd_pcm_drain(playback_handle); +} diff --git a/src/tool/main.c b/src/tool/main.c new file mode 100644 index 000000000..4e6b1adc6 --- /dev/null +++ b/src/tool/main.c @@ -0,0 +1,27 @@ +#include +#include +#include "alsa.c" + +#define SAMPLE_RATE 44100 + +int16_t quiet[SAMPLE_RATE], noisy[SAMPLE_RATE]; + +void main () { + + for (int i=0; i Date: Tue, 22 Nov 2011 03:26:43 +0100 Subject: [PATCH 293/296] integrate Odin's ALSA experiments into the Lumiera build requires ALSA as build dependency note: Debian package is libasound2-dev --- src/tool/SConscript | 7 ++++++ src/tool/alsa.c | 27 ++++++++++++++++++++--- src/tool/alsa.h | 54 +++++++++++++++++++++++++++++++++++++++++++++ src/tool/main.c | 28 ++++++++++++++++++++++- 4 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 src/tool/alsa.h diff --git a/src/tool/SConscript b/src/tool/SConscript index 85a858d5e..2d326feea 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -10,12 +10,19 @@ envSvg = env.Clone() envSvg.mergeConf(['librsvg-2.0']) envSvg.Append(LIBS=support_lib) +envSnd = env.Clone() +envSnd.mergeConf(['alsa']) + + +outputProbe = envSnd.Program('lumiera-output-probe' + , ['main.c', 'alsa.c'] + core, install=True) ## Odin's ALSA experiments luidgen = env.Program('luidgen', ['luidgen.c'] + support_lib, install=True) ## for generating Lumiera-UIDs rsvg = envSvg.Program('rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) # build additional test and administrative tools.... tools = [ env.Program('hello-world','hello.c', install=True) #### hello world (checks C build) + + outputProbe #### for output connection tests + luidgen + rsvg ] diff --git a/src/tool/alsa.c b/src/tool/alsa.c index e6c4afbba..d210693dd 100644 --- a/src/tool/alsa.c +++ b/src/tool/alsa.c @@ -1,6 +1,27 @@ -#include -#include -#include +/* + ALSA - sound output backend using the Advanced Linux Sound Architecture + + Copyright (C) Lumiera.org + 2011, Odin Omdal Hørthe + + 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 "alsa.h" #include diff --git a/src/tool/alsa.h b/src/tool/alsa.h new file mode 100644 index 000000000..30fdb75f7 --- /dev/null +++ b/src/tool/alsa.h @@ -0,0 +1,54 @@ +/* + ALSA.h - sound output backend using the Advanced Linux Sound Architecture + + Copyright (C) Lumiera.org + 2011, Odin Omdal Hørthe + + 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 alsa.h + ** Interfacing to ALSA sound output. + ** + ** @todo for now this header defines some functions used for experimentation with ALSA + ** + ** @see output-slot.hpp + */ + + +#ifndef TOOL_ALSA_H +#define TOOL_ALSA_H + + +#include + + + +size_t audio_offset(); + + +void audio_init(); + + +size_t audio_write(const void* data, size_t amount); + + +void audio_start(unsigned int rate, unsigned int channel_count); + + +void audio_stop(); + +#endif // TOOL_ALSA_H diff --git a/src/tool/main.c b/src/tool/main.c index 4e6b1adc6..e8a684705 100644 --- a/src/tool/main.c +++ b/src/tool/main.c @@ -1,6 +1,32 @@ +/* + OutputProbe - tool to investigate external output connections + + Copyright (C) Lumiera.org + 2011, Odin Omdal Hørthe + + 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 "alsa.h" + #include #include -#include "alsa.c" +#include + #define SAMPLE_RATE 44100 From 3be546a6b8df4dd1d35e689238ec26b09914bcb0 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 11 Jan 2012 22:31:03 +0100 Subject: [PATCH 294/296] RFC: bless scons as offical build system --- .../MakeSconsTheOfficialBuildSystem.txt | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 doc/devel/rfc_pending/MakeSconsTheOfficialBuildSystem.txt diff --git a/doc/devel/rfc_pending/MakeSconsTheOfficialBuildSystem.txt b/doc/devel/rfc_pending/MakeSconsTheOfficialBuildSystem.txt new file mode 100644 index 000000000..53958c986 --- /dev/null +++ b/doc/devel/rfc_pending/MakeSconsTheOfficialBuildSystem.txt @@ -0,0 +1,83 @@ +Make Scons the official build System +==================================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Final_ +*Date* _Mi 11 Jan 2012 21:45:58 CET_ +*Proposed by* Christian Thaeter +------------------------------------- + +******************************************************************************** +.Abstract +_Bless Scons the default build system for Lumiera._ +******************************************************************************** + +Description +----------- +//description: add a detailed description: + +So far we using autotools and scons in parallel. Over time the need arose to have one +reliable supported build system. This shall be scons. + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +// * first step ([green]#✔ done#) +// * second step [,yellow]#WIP# +Nothing to do except for releases scons *must* be working and all non functional +build systems will be stripped from releases (branches?). + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add a fact list/enumeration which make this suitable: +// * foo +// * bar ... + + + +Cons +^^^^ +// fact list of the known/considered bad implications: + + + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: + + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: + + + +Conclusion +---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + +Comments +-------- +//comments: append below + +.State -> Final +//add reason +Decided on the December 2011 Developer meeting. + Mi 11 Jan 2012 22:28:36 CET Christian Thaeter + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] From 0b2537bb897a6cd5a1f9c7e5760121af962292b9 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 11 Jan 2012 22:32:13 +0100 Subject: [PATCH 295/296] RFC: bless scons as offical build system, final --- .../{rfc_pending => rfc}/MakeSconsTheOfficialBuildSystem.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/devel/{rfc_pending => rfc}/MakeSconsTheOfficialBuildSystem.txt (100%) diff --git a/doc/devel/rfc_pending/MakeSconsTheOfficialBuildSystem.txt b/doc/devel/rfc/MakeSconsTheOfficialBuildSystem.txt similarity index 100% rename from doc/devel/rfc_pending/MakeSconsTheOfficialBuildSystem.txt rename to doc/devel/rfc/MakeSconsTheOfficialBuildSystem.txt From 55ff4e349cdfc9b0972fa3753ed6f9f7592df88a Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 12 Jan 2012 15:49:40 +0100 Subject: [PATCH 296/296] Meeting summary, January 2012 --- doc/devel/meeting_summary/2012-01-11.txt | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 doc/devel/meeting_summary/2012-01-11.txt diff --git a/doc/devel/meeting_summary/2012-01-11.txt b/doc/devel/meeting_summary/2012-01-11.txt new file mode 100644 index 000000000..cce9fbd6a --- /dev/null +++ b/doc/devel/meeting_summary/2012-01-11.txt @@ -0,0 +1,35 @@ +2012-01-11 Lumiera Developers Meeting +===================================== +:Author: cehteh +:Date: 2012-01-11 + +Jan 11, 2011 on #lumiera 20:00 + +__Participants__ + + * cehteh + * ichthyo + * benn + * raffa + +Conclusions +----------- + +. ichthyo removed most of the tiddly wikis, and worked the content into the website +. cehteh reports that Lumiera got another donation (75Eur), arrangements with + the ffis to get access (view) about the donations account are under way. We'll + ask donors then if they want to be published or stay anonym and will set up + a wiki page listing donations and expenses. +. ichthy rewrote the SCons build, as discussed last time +. cehteh writes a very short RfC, to document that we're using SCons for now. +. possibly no one going to LAC, too far away +. we discussed a link checker / link resolver for the website. + The idea is to have a semi automatic tool, which is used locally when + authoring website content to find cross references. +. benn and ichthyo follow up on the libregraphics magazine and try to get into + discussion with them and see what can be done within our limited time. + ichthyo respond to the mail, and put you (benn and ct) on CC. +. when it comes to have a working example for media file output, we stick to the + mainstream solutions ffmpeg and or gstreamer, but care not to lock ourselves + into a single solution. Concluded that we do this over plugin interfaces and + it mostly boils down to support ffmped .. and investigate something simpler too.